rake-commander 0.1.2 → 0.2.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 +4 -4
- data/.gitignore +8 -0
- data/.rubocop.yml +14 -8
- data/CHANGELOG.md +84 -4
- data/Gemfile +1 -1
- data/LICENSE +21 -0
- data/README.md +95 -3
- 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 -102
- 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 +50 -50
- data/lib/rake-commander/version.rb +1 -1
- data/lib/rake-commander.rb +4 -0
- data/rake-commander.gemspec +5 -2
- metadata +75 -7
- 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
|