rake-commander 0.1.4 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +8 -0
- data/.rubocop.yml +12 -8
- data/CHANGELOG.md +69 -4
- data/LICENSE +21 -0
- data/README.md +94 -2
- data/Rakefile +11 -13
- data/examples/01_basic_example.rb +28 -0
- data/examples/02_a_chainer_example.rb +66 -0
- data/examples/02_a_chainer_options_set.rb +8 -0
- data/examples/02_b_chained_example.rb +13 -0
- data/examples/03_a_chainer_plus_example.rb +34 -0
- data/examples/03_b_chained_plus_example.rb +17 -0
- data/examples/Examples.rake +7 -0
- data/examples/README.md +79 -0
- data/examples/libs/shell_helpers.rb +81 -0
- data/lib/rake-commander/base/class_auto_loader.rb +45 -7
- data/lib/rake-commander/base/class_helpers.rb +16 -61
- data/lib/rake-commander/base/class_inheritable.rb +122 -0
- data/lib/rake-commander/base/custom_error.rb +52 -0
- data/lib/rake-commander/base/object_helpers.rb +42 -0
- data/lib/rake-commander/base.rb +16 -2
- data/lib/rake-commander/option.rb +115 -25
- data/lib/rake-commander/options/arguments.rb +206 -94
- data/lib/rake-commander/options/description.rb +17 -0
- data/lib/rake-commander/options/error/base.rb +86 -0
- data/lib/rake-commander/options/error/handling.rb +106 -0
- data/lib/rake-commander/options/error/invalid_argument.rb +21 -0
- data/lib/rake-commander/options/error/invalid_option.rb +9 -0
- data/lib/rake-commander/options/error/missing_argument.rb +10 -0
- data/lib/rake-commander/options/error/missing_option.rb +48 -0
- data/lib/rake-commander/options/error/unknown_argument.rb +32 -0
- data/lib/rake-commander/options/error.rb +75 -10
- data/lib/rake-commander/options/name.rb +67 -23
- data/lib/rake-commander/options/result.rb +107 -0
- data/lib/rake-commander/options/set.rb +7 -1
- data/lib/rake-commander/options.rb +175 -98
- data/lib/rake-commander/patcher/README.md +79 -0
- data/lib/rake-commander/patcher/application/run_method.rb +46 -0
- data/lib/rake-commander/patcher/application/top_level_method.rb +74 -0
- data/lib/rake-commander/patcher/application.rb +16 -0
- data/lib/rake-commander/patcher/base.rb +45 -0
- data/lib/rake-commander/patcher/debug.rb +32 -0
- data/lib/rake-commander/patcher/helpers.rb +44 -0
- data/lib/rake-commander/patcher.rb +26 -0
- data/lib/rake-commander/rake_context/wrapper.rb +2 -0
- data/lib/rake-commander/rake_task.rb +49 -54
- data/lib/rake-commander/version.rb +1 -1
- data/lib/rake-commander.rb +4 -0
- data/rake-commander.gemspec +4 -1
- metadata +74 -6
- data/examples/basic.rb +0 -30
- data/lib/rake-commander/options/error_rely.rb +0 -58
@@ -0,0 +1,106 @@
|
|
1
|
+
class RakeCommander
|
2
|
+
module Options
|
3
|
+
module Error
|
4
|
+
module Handling
|
5
|
+
class << self
|
6
|
+
def included(base)
|
7
|
+
super(base)
|
8
|
+
base.extend RakeCommander::Base::ClassHelpers
|
9
|
+
base.extend RakeCommander::Base::ClassInheritable
|
10
|
+
base.extend ClassMethods
|
11
|
+
base.attr_inheritable :error_on_options, :error_on_options_handler
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
attr_reader :options_latest_error
|
17
|
+
|
18
|
+
# Whether it should trigger an error when there are `ARGV` option errors during `parse_options`
|
19
|
+
# @note
|
20
|
+
# 1. It triggers error by default when there are parsing option errors.
|
21
|
+
# 2. Even if a `handler` block is defined, if action is `false` it won't trigger error.
|
22
|
+
# 3. When specific errors are NOT specified, they will **fallback** to the action defined
|
23
|
+
# on the parent class `RakeCommander::Options::Error::Base`. This means that you can define
|
24
|
+
# a default behaviour for this.
|
25
|
+
# @raise [RakeCommander::Options::Error::Base] the specific option error that was raised.
|
26
|
+
# 1. when `action` is `true` (default)
|
27
|
+
# 2. when the `handler` is defined and returns `true`.
|
28
|
+
# @yield [error, argv, results, leftovers] do some stuff and decide if an error should be raised.
|
29
|
+
# @yieldparam error [RakeCommander::Options::Error::Base] the specific error.
|
30
|
+
# @yieldparam argv [Array<String>] arguments that were being parsed.
|
31
|
+
# @yieldparam results [Hash] the parsed options.
|
32
|
+
# @yieldparam leftovers [Array<String>] arguments of `argv` that the parser could not identify.
|
33
|
+
# @yieldreturn [Boolean] whether this should trigger an error or not.
|
34
|
+
# @param action [Boolean, Symbol] possible values are:
|
35
|
+
# 1. `:not_used` -> it will retrieve the currect action value
|
36
|
+
# 2. `true` (default) -> it switches `on` the exception triggering
|
37
|
+
# 3. `false` -> it will **print** the error and **exit** with status `1`
|
38
|
+
# 4. `:continue` -> it will continue with whatver it got (**use this at your own risk**)
|
39
|
+
# @param error [RakeCommander::Options::Error::Base:Class] or children thereof.
|
40
|
+
# @return [Boolean] whether this error is enabled.
|
41
|
+
def error_on_options(action = :not_used, error: RakeCommander::Options::Error::Base, &handler)
|
42
|
+
RakeCommander::Options::Error::Base.require_argument!(error, :error, accept_children: true)
|
43
|
+
@options_latest_error = nil
|
44
|
+
@error_on_options ||= {}
|
45
|
+
@error_on_options[error] = action if action != :not_used
|
46
|
+
|
47
|
+
if block_given?
|
48
|
+
error_on_options_handler(error, &handler)
|
49
|
+
@error_on_options[error] ||= true
|
50
|
+
end
|
51
|
+
|
52
|
+
return self unless block_given? || action != :not_used
|
53
|
+
# default value
|
54
|
+
@error_on_options[error] = true unless @error_on_options[error] == false
|
55
|
+
@error_on_options[error]
|
56
|
+
end
|
57
|
+
|
58
|
+
# @see #error_on_options
|
59
|
+
def error_on_leftovers(action = :not_used, &handler)
|
60
|
+
error_on_options(action, error: RakeCommander::Options::Error::UnknownArgument, &handler)
|
61
|
+
end
|
62
|
+
|
63
|
+
# @return [Boolean] whether there is an error `action` defined for `error`
|
64
|
+
def error_on_options?(error = RakeCommander::Options::Error::Base)
|
65
|
+
RakeCommander::Options::Error::Base.require_argument!(error, :error, accept_children: true)
|
66
|
+
_default_action = error_on_options
|
67
|
+
@error_on_options.key?(error) || error_on_options_handler.key?(error)
|
68
|
+
end
|
69
|
+
|
70
|
+
protected
|
71
|
+
|
72
|
+
# Provide error for the given block.
|
73
|
+
# @return [String<Array>] the result of `yield` or `leftovers` if there was an error
|
74
|
+
# where the `action` was defined as `:continue`
|
75
|
+
def with_error_handling(argv, results, leftovers)
|
76
|
+
yield
|
77
|
+
rescue RakeCommander::Options::Error::Base => e
|
78
|
+
@options_latest_error = e
|
79
|
+
eklass = e.class
|
80
|
+
# Fallback to generic error handling if specific error action is not defined
|
81
|
+
eklass = eklass.superclass unless error_on_options?(eklass)
|
82
|
+
action = error_on_options(error: eklass)
|
83
|
+
|
84
|
+
# here is where we ignore the handler (when !action == `true`)
|
85
|
+
raise unless !action || handler = error_on_options_handler(eklass)
|
86
|
+
raise if handler&.call(e, argv, results, leftovers)
|
87
|
+
return leftovers if action == :continue
|
88
|
+
puts e.message
|
89
|
+
# https://stackoverflow.com/a/23340693/4352306
|
90
|
+
exit 1
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def error_on_options_handler(error = :not_used, &handler)
|
96
|
+
@error_on_options_handler ||= {}
|
97
|
+
return @error_on_options_handler if error == :not_used
|
98
|
+
RakeCommander::Options::Error::Base.require_argument!(error, :error, accept_children: true)
|
99
|
+
@error_on_options_handler[error] = handler if block_given?
|
100
|
+
@error_on_options_handler[error]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class RakeCommander
|
2
|
+
module Options
|
3
|
+
module Error
|
4
|
+
class InvalidArgument < RakeCommander::Options::Error::Base
|
5
|
+
option_regex(/invalid argument: (?<option>.+)/i.freeze)
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def to_message(value)
|
10
|
+
return super unless opt = option
|
11
|
+
case value
|
12
|
+
when OptionParser::InvalidArgument
|
13
|
+
super("invalid option argument: #{opt.name_hyphen} (#{opt.short_hyphen})")
|
14
|
+
else
|
15
|
+
super
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class RakeCommander
|
2
|
+
module Options
|
3
|
+
module Error
|
4
|
+
# Relates to options with missing required argument (when there's no `default` value)
|
5
|
+
class MissingArgument < RakeCommander::Options::Error::Base
|
6
|
+
option_regex(/missing(?: required|) argument: (?<option>.+)/i.freeze)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class RakeCommander
|
2
|
+
module Options
|
3
|
+
module Error
|
4
|
+
# Relates to the `required` parameter when defining an option.
|
5
|
+
class MissingOption < RakeCommander::Options::Error::Base
|
6
|
+
def initialize(value = nil, from: nil)
|
7
|
+
super("missing required option: #{to_message(value)}", from: from)
|
8
|
+
end
|
9
|
+
|
10
|
+
def options
|
11
|
+
super | to_options(@value)
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
def to_message(value)
|
17
|
+
case value
|
18
|
+
when RakeCommander::Option
|
19
|
+
"#{value.name_hyphen} (#{value.short_hyphen})"
|
20
|
+
when Hash
|
21
|
+
to_message(value.values.uniq)
|
22
|
+
when Array
|
23
|
+
value.map do |v|
|
24
|
+
v.is_a?(RakeCommander::Option)? to_message(v) : v
|
25
|
+
end.join(', ')
|
26
|
+
else
|
27
|
+
super
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def to_options(value)
|
34
|
+
case value
|
35
|
+
when RakeCommander::Option
|
36
|
+
[value]
|
37
|
+
when Array
|
38
|
+
value.select {|v| v.is_a?(RakeCommander::Option)}
|
39
|
+
when Hash
|
40
|
+
to_options(value.values)
|
41
|
+
else
|
42
|
+
[]
|
43
|
+
end.compact
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class RakeCommander
|
2
|
+
module Options
|
3
|
+
module Error
|
4
|
+
# Relates to `OptionParser#parse` output (**leftovers**)
|
5
|
+
class UnknownArgument < RakeCommander::Options::Error::Base
|
6
|
+
def initialize(value = nil, from: nil)
|
7
|
+
super("unknown arguments: #{to_message(value)}", from: from)
|
8
|
+
end
|
9
|
+
|
10
|
+
def leftovers
|
11
|
+
case @value
|
12
|
+
when Array
|
13
|
+
@value
|
14
|
+
else
|
15
|
+
[]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def to_message(value)
|
22
|
+
case value
|
23
|
+
when Array
|
24
|
+
value.map {|v| "'#{v}'"}.join(', ')
|
25
|
+
else
|
26
|
+
super
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -1,18 +1,83 @@
|
|
1
|
-
require_relative '
|
1
|
+
require_relative 'error/base'
|
2
|
+
require_relative 'error/missing_argument'
|
3
|
+
require_relative 'error/invalid_argument'
|
4
|
+
require_relative 'error/invalid_option'
|
5
|
+
require_relative 'error/missing_option'
|
6
|
+
require_relative 'error/unknown_argument'
|
7
|
+
require_relative 'error/handling'
|
8
|
+
|
2
9
|
class RakeCommander
|
3
10
|
module Options
|
4
|
-
|
5
|
-
|
6
|
-
|
11
|
+
module Error
|
12
|
+
class << self
|
13
|
+
def included(base)
|
14
|
+
super(base)
|
15
|
+
base.send :include, RakeCommander::Options::Error::Handling
|
16
|
+
base.extend ClassMethods
|
17
|
+
end
|
7
18
|
end
|
8
|
-
end
|
9
19
|
|
10
|
-
|
11
|
-
|
12
|
-
|
20
|
+
module ClassMethods
|
21
|
+
# Re-open method to add all the error handling.
|
22
|
+
# @see RakeCommander::Options::Result
|
23
|
+
def parse_options(argv = ARGV, results: {}, leftovers: [], &block)
|
24
|
+
with_error_handling(argv, results, leftovers) do
|
25
|
+
super.tap do |_|
|
26
|
+
check_on_leftovers(leftovers)
|
27
|
+
check_required_presence(results)
|
28
|
+
end
|
29
|
+
rescue OptionParser::InvalidOption => e
|
30
|
+
eklass = RakeCommander::Options::Error::InvalidOption
|
31
|
+
raise eklass.new(e, from: self), nil, cause: nil
|
32
|
+
rescue OptionParser::MissingArgument => e
|
33
|
+
eklass = RakeCommander::Options::Error::MissingArgument
|
34
|
+
opt = error_option(e, eklass)
|
35
|
+
msg = e.message
|
36
|
+
msg = "missing required argument: #{opt.name_hyphen} (#{opt.short_hyphen})" if opt
|
37
|
+
raise eklass.new(from: self, option: opt), msg, cause: nil
|
38
|
+
rescue OptionParser::InvalidArgument => e
|
39
|
+
eklass = RakeCommander::Options::Error::InvalidArgument
|
40
|
+
opt = error_option(e, eklass)
|
41
|
+
raise eklass.new(e, from: self, option: opt), nil, cause: nil unless opt&.argument_required?
|
42
|
+
eklass = RakeCommander::Options::Error::MissingArgument
|
43
|
+
msg = "missing required argument in option: #{opt.name_hyphen} (#{opt.short_hyphen})"
|
44
|
+
raise eklass.new(from: self, option: opt), msg, cause: nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
13
49
|
|
14
|
-
|
15
|
-
|
50
|
+
# Helper to retrieve an existing `RakeCommander::Option` out of a
|
51
|
+
# `OptionParser` error.
|
52
|
+
# @param e [OptionParser:Error] containing the original error `message`
|
53
|
+
# @param eklass [RakeCommander::Options::Error:Base::Class] the error class to retrive the option key
|
54
|
+
# @return [RakeCommander::Option, NilClass]
|
55
|
+
def error_option(err, eklass)
|
56
|
+
return false unless option_sym = eklass.option_sym(err.message)
|
57
|
+
options_hash(with_implicit: true)[option_sym]
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
# It implements the logic defined by `error_on_leftovers`.
|
63
|
+
# @param leftovers [Array<String>]
|
64
|
+
# @param results [Hash] the parsed options
|
65
|
+
# @return [Hash] the results (same object)
|
66
|
+
def check_on_leftovers(leftovers)
|
67
|
+
return if leftovers.empty?
|
68
|
+
eklass = RakeCommander::Options::Error::UnknownArgument
|
69
|
+
raise eklass.new(leftovers, from: self)
|
70
|
+
end
|
71
|
+
|
72
|
+
# It throws an exception if any of the required options
|
73
|
+
# is missing in results
|
74
|
+
def check_required_presence(results)
|
75
|
+
missing = options.select(&:required?).reject do |opt|
|
76
|
+
results.key?(opt.short) || results.key?(opt.name)
|
77
|
+
end
|
78
|
+
raise RakeCommander::Options::Error::MissingOption.new(missing, from: self) unless missing.empty?
|
79
|
+
end
|
80
|
+
end
|
16
81
|
end
|
17
82
|
end
|
18
83
|
end
|
@@ -1,15 +1,17 @@
|
|
1
1
|
class RakeCommander
|
2
2
|
module Options
|
3
3
|
module Name
|
4
|
+
BOOLEAN_TOKEN = '[no-]'.freeze
|
4
5
|
# Substitions
|
5
6
|
HYPHEN_START_REGEX = /^-+/.freeze
|
6
7
|
HYPEN_REGEX = /-+/.freeze
|
7
8
|
UNDERSCORE_REGEX = /_+/.freeze
|
8
|
-
|
9
|
-
# Checkers
|
9
|
+
WORD_DELIMITER = /[\s=]+/.freeze
|
10
|
+
# Checkers / Capturers
|
10
11
|
OPTIONAL_REGEX = /\[\w+\]$/.freeze
|
11
12
|
SINGLE_HYPHEN_REGEX = /^-(?<options>[^- ][^ ]*)/.freeze
|
12
|
-
DOUBLE_HYPHEN_REGEX =
|
13
|
+
DOUBLE_HYPHEN_REGEX = /^(?:--\[?no-\]?|--)(?<option>[^- ][^ \r\n]*).*$/.freeze
|
14
|
+
BOOLEAN_NAME_REGEX = /^[^ ]*#{Regexp.escape(BOOLEAN_TOKEN)}[^ ]{2,}/.freeze
|
13
15
|
|
14
16
|
# @return [Boolean]
|
15
17
|
def single_hyphen?(value)
|
@@ -23,32 +25,68 @@ class RakeCommander
|
|
23
25
|
!!value.to_s.match(DOUBLE_HYPHEN_REGEX)
|
24
26
|
end
|
25
27
|
|
28
|
+
# @return [Boolean] whether the name has the boolean switch `[no-]`
|
29
|
+
def boolean_name?(value)
|
30
|
+
return false unless value.respond_to?(:to_s)
|
31
|
+
!!value.to_s.match(BOOLEAN_NAME_REGEX)
|
32
|
+
end
|
33
|
+
|
26
34
|
# @param strict [Boolean] whether hyphen is required when declaring an option `short`
|
27
35
|
# @return [Boolean]
|
28
36
|
def valid_short?(value, strict: false)
|
29
|
-
return false unless value.respond_to?(:to_s)
|
30
|
-
|
31
|
-
|
37
|
+
return false unless value.respond_to?(:to_s)
|
38
|
+
value = value.to_s.strip
|
39
|
+
return false if value.empty?
|
40
|
+
return false if strict && !single_hyphen?(value)
|
41
|
+
value = value.gsub(HYPHEN_START_REGEX, '')
|
42
|
+
value.length == 1
|
32
43
|
end
|
33
44
|
|
34
45
|
# @param strict [Boolean] whether hyphen is required when declaring an option `name`
|
35
46
|
# @return [Boolean]
|
36
47
|
def valid_name?(value, strict: false)
|
37
|
-
return false unless value.respond_to?(:to_s)
|
38
|
-
|
48
|
+
return false unless value.respond_to?(:to_s)
|
49
|
+
value = value.to_s.strip
|
50
|
+
return false if value.empty?
|
51
|
+
return false if strict && !double_hyphen?(value)
|
39
52
|
name_sym(value).to_s.length > 1
|
40
53
|
end
|
41
54
|
|
42
|
-
#
|
43
|
-
# @
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
55
|
+
# Modifies `args` and returns the short candidate
|
56
|
+
# @param args [Array<String, Symbol>]
|
57
|
+
# @return [String, Symbol] the short candidate
|
58
|
+
def capture_arguments_short!(args, strict: true, symbol: false)
|
59
|
+
capture_argument_with!(args) do |arg|
|
60
|
+
next false unless arg.is_a?(String) || arg.is_a?(Symbol)
|
61
|
+
next false if symbol && !arg.is_a?(Symbol)
|
62
|
+
valid_short?(arg, strict: strict)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Modifies `args` and returns the name candidate
|
67
|
+
# @param args [Array<String, Symbol>]
|
68
|
+
# @return [String, Symbol] the name candidate
|
69
|
+
def capture_arguments_name!(args, strict: true, symbol: false)
|
70
|
+
capture_argument_with!(args) do |arg|
|
71
|
+
next false unless arg.is_a?(String) || arg.is_a?(Symbol)
|
72
|
+
next false if symbol && !arg.is_a?(Symbol)
|
73
|
+
valid_name?(arg, strict: strict)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Modifies `args` and returns the arg candidate
|
78
|
+
# @param args [Array<String, Symbol>]
|
79
|
+
# @return [String, Symbol, NilClass] the arg candidate
|
80
|
+
def capture_argument_with!(args)
|
81
|
+
raise ArgumentError, "Expecting Array. Given: #{args.class}" unless args.is_a?(Array)
|
82
|
+
args.dup.find.with_index do |arg, i|
|
83
|
+
yield(arg).tap do |valid|
|
84
|
+
next unless valid
|
85
|
+
args.slice!(i)
|
86
|
+
return arg
|
87
|
+
end
|
88
|
+
end
|
89
|
+
nil
|
52
90
|
end
|
53
91
|
|
54
92
|
# Converter
|
@@ -57,7 +95,8 @@ class RakeCommander
|
|
57
95
|
# @return [Symbol, NilClass]
|
58
96
|
def short_sym(value)
|
59
97
|
return nil unless value
|
60
|
-
value = value.to_s.gsub(
|
98
|
+
value = value.to_s.gsub(BOOLEAN_TOKEN, '')
|
99
|
+
value = value.gsub(HYPHEN_START_REGEX, '')
|
61
100
|
return nil unless value = value.chars.first
|
62
101
|
value.to_sym
|
63
102
|
end
|
@@ -74,20 +113,25 @@ class RakeCommander
|
|
74
113
|
return nil unless value
|
75
114
|
value = value.to_s.gsub(HYPHEN_START_REGEX, '')
|
76
115
|
value = value.gsub(HYPEN_REGEX, '_')
|
77
|
-
value = value.gsub(
|
116
|
+
value = value.gsub(WORD_DELIMITER, ' ')
|
78
117
|
return nil if value.empty?
|
79
118
|
value.to_sym
|
80
119
|
end
|
81
120
|
|
82
121
|
# It's like `#name_sym` but it only gets the option name.
|
122
|
+
# @note
|
123
|
+
# 1. It also removes the boolean token `[no-]`
|
83
124
|
# @example
|
84
125
|
# * `"--there-we-go ARGUMENT"` becomes `:there_we_go`
|
126
|
+
# * `"--[no]-verbose"` becomes `:verbose`
|
85
127
|
# @see #name_sym
|
86
128
|
# @return [Symbol, NilClass]
|
87
129
|
def name_word_sym(value)
|
88
130
|
return nil unless value = name_sym(value)
|
131
|
+
value = value.to_s.gsub(BOOLEAN_TOKEN, '')
|
132
|
+
|
89
133
|
return nil unless value = name_words(value).first
|
90
|
-
value.to_sym
|
134
|
+
value.downcase.to_sym
|
91
135
|
end
|
92
136
|
|
93
137
|
# @return [String, NilClass] it returns the hyphened (`-`) version of a short `value`
|
@@ -98,7 +142,7 @@ class RakeCommander
|
|
98
142
|
|
99
143
|
# Gets the actual name of the option. First word.
|
100
144
|
# @example
|
101
|
-
# * `"--there-we-go ARGUMENT"` becomes `"--there-we-go"`
|
145
|
+
# * `"--there-we-go ARGUMENT"` becomes `"--there-we-go ARGUMENT"`
|
102
146
|
# * `"there-we-go"` becomes `"--there-we-go"`
|
103
147
|
# * `:there_we_go` becomes `"--there-we-go"`
|
104
148
|
# @return [String, NilClass] option `name` alone double hypened (`--`)
|
@@ -153,7 +197,7 @@ class RakeCommander
|
|
153
197
|
def name_words(value)
|
154
198
|
return nil unless value
|
155
199
|
value = value.to_s.gsub(HYPHEN_START_REGEX, '')
|
156
|
-
value.to_s.split(
|
200
|
+
value.to_s.split(WORD_DELIMITER)
|
157
201
|
end
|
158
202
|
end
|
159
203
|
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
class RakeCommander
|
2
|
+
module Options
|
3
|
+
module Result
|
4
|
+
class << self
|
5
|
+
def included(base)
|
6
|
+
super(base)
|
7
|
+
base.extend ClassMethods
|
8
|
+
base.attr_inheritable :options_with_defaults
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
# Configuration Setting.
|
14
|
+
# @return [Boolean] whether results should include options defined
|
15
|
+
# with a default, regarless if they are invoked
|
16
|
+
def options_with_defaults(value = nil)
|
17
|
+
if value.nil?
|
18
|
+
@options_with_defaults || false
|
19
|
+
else
|
20
|
+
@options_with_defaults = !!value
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# It **re-opens** the method and adds a middleware to gather and return
|
25
|
+
# the results of the parsing (including the `leftovers`)
|
26
|
+
# @note this extends the method parameters and changes the returned value.
|
27
|
+
# @param leftovers [Array<String>] see RakeCommander::Options#parse_options`
|
28
|
+
# @param results [Hash] with `short` option as `key` and final value as `value`.
|
29
|
+
# @see `RakeCommander::Options#parse_options`
|
30
|
+
def parse_options(argv = ARGV, results: {}, leftovers: [], &middleware)
|
31
|
+
leftovers.push(*super(argv, &results_collector(results, &middleware)))
|
32
|
+
end
|
33
|
+
|
34
|
+
# **Extend** method to ensure options are parsed before calling task.
|
35
|
+
# This only happens if `task_method` has it's context (binding) with
|
36
|
+
# an instance object of this class.
|
37
|
+
# @note
|
38
|
+
# 1. This allows stop before invoking the task, may there be options
|
39
|
+
# that have this effect (i.e. `-h`)
|
40
|
+
# 2. We use `task_context` to open up extensibility.
|
41
|
+
# @todo think if it should rather raise an `ArgumentError` when the task
|
42
|
+
# was not defined in an instance object of his class.
|
43
|
+
def install_task(&task_method)
|
44
|
+
super(&task_context(&task_method))
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
|
49
|
+
# Invoke `options` parsing before calling the task.
|
50
|
+
# @return [Proc] our wrapped task block.
|
51
|
+
def task_context(&task_method)
|
52
|
+
instance = eval('self', task_method.binding, __FILE__, __LINE__)
|
53
|
+
return task_method unless instance.is_a?(self)
|
54
|
+
proc do |*task_args|
|
55
|
+
# launch `ARGV` parsing
|
56
|
+
instance.options
|
57
|
+
task_method.call(*task_args)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# Expects a block that should do the final call to `OptionParser#parse`.
|
64
|
+
# It is invoked on each option parsing (so only when that option is invoked).
|
65
|
+
# @note if an invoked option comes empty (`nil`), it uses the `default` value.
|
66
|
+
# @return [Proc] the results collector that wraps the middleware.
|
67
|
+
def results_collector(results, &middleware)
|
68
|
+
results = result_defaults(results)
|
69
|
+
proc do |value, default, short, name|
|
70
|
+
middleware&.call(value, default, short, name)
|
71
|
+
results[short] = value.nil?? default : value
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Based on `required` options, it sets the `default`
|
76
|
+
def result_defaults(results = {})
|
77
|
+
results.tap do |res_def|
|
78
|
+
options.select do |opt|
|
79
|
+
(options_with_defaults && opt.default?) \
|
80
|
+
|| (opt.required? && opt.default?)
|
81
|
+
end.each do |opt|
|
82
|
+
res_def[opt.short] = opt.default
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Launches the options parsing of this class.
|
89
|
+
# @return [Hash] keyed by short.
|
90
|
+
def options(argv = ARGV, &block)
|
91
|
+
return @options if instance_variable_defined?(:@options)
|
92
|
+
@options = {}
|
93
|
+
self.class.parse_options(argv, results: @options, leftovers: options_leftovers, &block)
|
94
|
+
@options
|
95
|
+
end
|
96
|
+
|
97
|
+
# The options part (so after `--`) that was NOT processed
|
98
|
+
# by the `OptionParser`. They are therefore unknown parameters.
|
99
|
+
# @note an unknown option (i.e. `-f something`, `--foo something`) would trigger an invalid option error.
|
100
|
+
# This means that the `leftovers` can only refer to arguments not paired to options that receive
|
101
|
+
# parameters (i.e. `--no-foo something`).
|
102
|
+
def options_leftovers
|
103
|
+
@options_leftovers ||= []
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -6,9 +6,15 @@ class RakeCommander
|
|
6
6
|
class << self
|
7
7
|
include Enumerable
|
8
8
|
|
9
|
+
# Name of the `Options::Set`
|
10
|
+
def name(value = :not_used)
|
11
|
+
return @name if value == :not_used
|
12
|
+
@name = value.to_sym
|
13
|
+
end
|
14
|
+
|
9
15
|
def each(&block)
|
10
16
|
return to_enum(:each) unless block
|
11
|
-
options.
|
17
|
+
options.each(&block)
|
12
18
|
end
|
13
19
|
end
|
14
20
|
end
|