rake-commander 0.1.2

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.
@@ -0,0 +1,153 @@
1
+ class RakeCommander
2
+ @option_struct ||= Struct.new(:short, :name)
3
+ class Option < @option_struct
4
+ extend RakeCommander::Base::ClassHelpers
5
+ extend RakeCommander::Options::Name
6
+
7
+ attr_accessor :desc, :default
8
+ attr_writer :type_coertion, :required
9
+ attr_reader :name_full
10
+
11
+ def initialize(short, name, *args, **kargs, &block)
12
+ raise ArgumentError, "A short of one letter should be provided. Given: #{short}" unless self.class.valid_short?(short)
13
+ raise ArgumentError, "A name should be provided. Given: #{name}" unless self.class.valid_name?(name)
14
+
15
+ @name_full = name
16
+ super(short, name)
17
+ @default = kargs[:default] if kargs.key?(:default)
18
+ @desc = kargs[:desc] if kargs.key?(:desc)
19
+ @required = kargs[:required] if kargs.key?(:required)
20
+ @other_args = args
21
+ @original_block = block
22
+ yield(self) if block_given?
23
+ configure_other
24
+ end
25
+
26
+ def required?
27
+ !!@required
28
+ end
29
+
30
+ # @return [Symbol]
31
+ def short
32
+ self.class.short_sym(super)
33
+ end
34
+
35
+ # @return [String]
36
+ def short_hyphen
37
+ self.class.short_hyphen(short)
38
+ end
39
+
40
+ # @return [Symbol]
41
+ def name
42
+ self.class.name_word_sym(super)
43
+ end
44
+
45
+ # @return [String]
46
+ def name_hyphen
47
+ self.class.name_hyphen(name_full)
48
+ end
49
+
50
+ # @param [Boolean] whether this option allows an argument
51
+ def argument?
52
+ self.class.name_argument?(name_full)
53
+ end
54
+
55
+ # @param [String, Nil] the argument, may it exist
56
+ def argument
57
+ return nil unless argument?
58
+ self.class.name_argument(name_full)
59
+ end
60
+
61
+ # @param [Boolean] If there was an argument, whether it is required
62
+ def argument_required?
63
+ self.class.argument_required?(argument)
64
+ end
65
+
66
+ # @return [Class, NilClass]
67
+ def type_coertion
68
+ @type_coertion || (default? && default.class)
69
+ end
70
+
71
+ # @return [Boolean]
72
+ def default?
73
+ instance_variable_defined?(:@default)
74
+ end
75
+
76
+ # Adds this option's switch to the `OptionParser`
77
+ # @note it allows to add a `middleware` block that will be called at `parse` runtime
78
+ def add_switch(opts_parser, where: :base, &middleware)
79
+ raise "Expecting OptionParser. Given: #{opts_parser.class}" unless opts_parser.is_a?(OptionParser)
80
+ case where
81
+ when :head, :top
82
+ opts_parser.on_head(*switch_args, &option_block(&middleware))
83
+ when :tail, :end
84
+ opts_parser.on_tail(*switch_args, &option_block(&middleware))
85
+ else # :base
86
+ opts_parser.on(*switch_args, &option_block(&middleware))
87
+ end
88
+ opts_parser
89
+ end
90
+
91
+ # @return [Array<Variant>]
92
+ def switch_args
93
+ configure_other
94
+ args = [short_hyphen, name_hyphen]
95
+ if str = switch_desc
96
+ args << str
97
+ end
98
+ args << type_coertion if type_coertion
99
+ args
100
+ end
101
+
102
+ private
103
+
104
+ # Called on parse runtime
105
+ def option_block(&middleware)
106
+ block_extra_args = [default, short, name]
107
+ proc do |value|
108
+ args = block_extra_args.dup.unshift(value)
109
+ @original_block&.call(*args)
110
+ middleware&.call(*args)
111
+ end
112
+ end
113
+
114
+ def switch_desc
115
+ val = "#{desc}#{default_desc}"
116
+ return nil if val.empty?
117
+ val
118
+ end
119
+
120
+ def default_desc
121
+ return nil unless default?
122
+ str = "Default: '#{default}'"
123
+ if desc && !desc.downcase.include?('default')
124
+ str = desc.end_with?('.') ? " #{str}" : ". #{str}"
125
+ end
126
+ str
127
+ end
128
+
129
+ def other_args(*args)
130
+ @other_args ||= []
131
+ if args.empty?
132
+ @other_args
133
+ else
134
+ @other_args.push(*args)
135
+ end
136
+ end
137
+
138
+ # It consumes `other_args`, to prevent direct overrides to be overriden by it.
139
+ def configure_other
140
+ if type = other_args.find {|arg| arg.is_a?(Class)}
141
+ self.type_coertion = type
142
+ other_args.delete(type)
143
+ end
144
+ if value = other_args.find {|arg| arg.is_a?(String)}
145
+ self.desc = value
146
+ other_args.dup.each do |val|
147
+ other_args.delete(val) if val.is_a?(String)
148
+ end
149
+ end
150
+ nil
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,116 @@
1
+ class RakeCommander
2
+ module Options
3
+ # Offers helpers to treat `ARGV`
4
+ module Arguments
5
+ include RakeCommander::Options::Name
6
+
7
+ # Options with arguments should not take another option as value.
8
+ # `OptionParser` can do this even if the the argument is optional.
9
+ # This method re-arranges the arguments based on options that receive parameters,
10
+ # provided that an option is not taken as a value of a previous option that accepts arguments.
11
+ # If an option with argument is missing the argument, but has a `default` value,
12
+ # that `default` value will be inserted after the option in the array
13
+ # to prevent the `OptionParser::MissingArgument` error to stop the parsing process.
14
+ # @note
15
+ # 1. Any word or letter with _hypen_ -`` or _double hypen_ `--` is interpreted as option(s)
16
+ # 2. To overcome this limitation, you may enclose in double quotes and argument with
17
+ # that start (i,e, `"--argument"`).
18
+ # @example
19
+ # 1. `-abc ARGUMENT` where only `c` receives the argument becomes `-ab -c ARGUMENT`
20
+ # 3. `-abc ARGUMENT` where `b` and `c` are argument receivers becomes `-a -b nil -c ARGUMENT`
21
+ # 2. `-acb ARGUMENT` where only `c` receives the argument becomes `-a -c nil -b ARGUMENT`
22
+ # 4. `-c --some-option ARGUMENT` where both options receive argument, becomes `-c nil --some-option ARGUMENT`
23
+ # 5. `-c --some-option -d ARGUMENT` where both options receive argument, becomes `-c nil --some-option nil -d ARGUMENT`
24
+ # 6. `-cd ARGUMENT` where `c` default is `"yeah"`, becomes `-c yeah -d ARGUMENT`
25
+ # @param argv [Array<String>]
26
+ # @param options [Hash] the defined `RakeCommander::Option` to re-arrange `argv` with.
27
+ # @return [Array<String>] the re-arranged `argv`
28
+ def pre_parse_arguments(argv = ARGV, options)
29
+ pre_parsed = explicit_argument_options(argv, options)
30
+ compact_short = ''
31
+ pre_parsed.each_with_object([]) do |(opt_ref, args), out|
32
+ next out.push(*args) unless opt_ref.is_a?(Symbol)
33
+ is_short = opt_ref.to_s.length == 1
34
+ next compact_short << opt_ref.to_s if is_short && args.empty?
35
+ out.push("-#{compact_short}") unless compact_short.empty?
36
+ compact_short = ''
37
+ opt_str = is_short ? "-#{opt_ref}" : name_hyphen(opt_ref)
38
+ out.push(opt_str, *args)
39
+ end.tap do |out|
40
+ out.push("-#{compact_short}") unless compact_short.empty?
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ # @example the output is actually a Hash, keyed by the Symbol of the option (short or name)
47
+ # 1. `-abc ARGUMENT` where only `c` receives the argument becomes `:a :b :c ARGUMENT`
48
+ # 3. `-abc ARGUMENT` where `b` and `c` are argument receivers becomes `:a :b nil :c ARGUMENT`
49
+ # 2. `-acb ARGUMENT` where only `c` receives the argument becomes `:a :c nil :b ARGUMENT`
50
+ # 4. `-c --some-option ARGUMENT` where both options receive argument, becomes `:c nil :some_option ARGUMENT`
51
+ # 5. `-c --some-option -d ARGUMENT` where first two options receive argument, becomes `:c nil :some_option nil :d ARGUMENT`
52
+ # 6. `-cd ARGUMENT` where `c` default is `"yeah"`, becomes `:c yeah :d ARGUMENT`
53
+ # @return [Hash<Symbol, Array>]
54
+ def explicit_argument_options(argv, options)
55
+ decoupled = decluster_shorts_n_names_to_sym(argv)
56
+ grouped = group_symbols_with_strings(decoupled)
57
+ normalized = insert_missing_argument_to_groups(grouped, options)
58
+ normalized.each_with_object({}) do |group, pre_parsed|
59
+ opt_ref = group.first.is_a?(Symbol)? group.shift : nil
60
+ pre_parsed[opt_ref] = group
61
+ end
62
+ end
63
+
64
+ # It adds the missing argument to options that expect it.
65
+ # @note it uses `default` if present, and `nil` otherwise.
66
+ # @param groups [@see #pair_symbols_with_strings]
67
+ def insert_missing_argument_to_groups(groups, options)
68
+ groups.each do |group|
69
+ args = group.dup
70
+ opt_ref = args.shift
71
+ next unless args.empty?
72
+ next unless opt_ref.is_a?(Symbol)
73
+ next unless opt = options[opt_ref]
74
+ next unless opt.argument?
75
+ next group.push(opt.default) if opt.default?
76
+ group.push(nil)
77
+ end
78
+ end
79
+
80
+ # @return [Array<Array>] where the first element of each `Array` is a symbol
81
+ # followed by one or more `String`.
82
+ def group_symbols_with_strings(argv)
83
+ [].tap do |out|
84
+ curr_ary = nil
85
+ argv.each do |arg|
86
+ if arg.is_a?(Symbol)
87
+ out << (curr_ary = [arg])
88
+ else # must be `String`
89
+ out << (curr_ary = []) unless curr_ary
90
+ curr_ary << arg
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ # It splits `argv` compacted shorts into their `Symbol` version.
97
+ # Symbolizes option `names` (long version).
98
+ # @return [Array<String, Symbol>] where symbols are options and strings arguments.
99
+ def decluster_shorts_n_names_to_sym(argv)
100
+ argv.each_with_object([]) do |arg, out|
101
+ if single_hyphen?(arg) # short option(s)
102
+ options = arg.match(SINGLE_HYPHEN_REGEX)[:options]
103
+ options.split('').each do |short|
104
+ out << short_sym(short)
105
+ end
106
+ elsif double_hyphen?(arg) # name option
107
+ name = arg.match(DOUBLE_HYPHEN_REGEX)[:option]
108
+ out << name_sym(name)
109
+ else # argument
110
+ out << arg
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,18 @@
1
+ require_relative 'error_rely'
2
+ class RakeCommander
3
+ module Options
4
+ class MissingOption < RakeCommander::Options::ErrorRely
5
+ def initialize(value)
6
+ super("missing required option: #{to_description(value)}")
7
+ end
8
+ end
9
+
10
+ class MissingArgument < RakeCommander::Options::ErrorRely
11
+ OPTION_REGEX = /missing (?:required|) argument: (?<option>.+)/i.freeze
12
+ end
13
+
14
+ class InvalidArgument < RakeCommander::Options::ErrorRely
15
+ OPTION_REGEX = /invalid argument: (?<option>.+)/i.freeze
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,58 @@
1
+ class RakeCommander
2
+ module Options
3
+ # Relies between OptionParser and RakeCommander errors
4
+ class ErrorRely < StandardError
5
+ extend RakeCommander::Options::Name
6
+
7
+ OPTION_REGEX = /(?:argument|option): (?<option>.+)/i.freeze
8
+
9
+ def initialize(value)
10
+ case value
11
+ when OptionParser::MissingArgument, OptionParser::InvalidArgument
12
+ super(value.message)
13
+ when String
14
+ super(value)
15
+ else
16
+ raise ArgumentError, "Expecting String or OptionParser error. Given: #{value.class}"
17
+ end
18
+ end
19
+
20
+ def name?
21
+ option_sym.to_s.length > 1
22
+ end
23
+
24
+ def short?
25
+ option_sym.to_s.length == 1
26
+ end
27
+
28
+ def option_sym
29
+ return @option_sym if @option_sym
30
+ return nil unless match = message.match(self.class::OPTION_REGEX)
31
+ option = match[:option]
32
+ @option_sym = \
33
+ if option.length > 1
34
+ self.class.name_word_sym(option)
35
+ else
36
+ self.class.short_sym(option)
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def to_description(value)
43
+ case value
44
+ when Hash
45
+ to_description(value.values.uniq)
46
+ when Array
47
+ value.map do |v|
48
+ to_description(v)
49
+ end.join(', ')
50
+ when RakeCommander::Option
51
+ "#{value.name_hyphen} (#{value.short_hyphen})"
52
+ else
53
+ value
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,160 @@
1
+ class RakeCommander
2
+ module Options
3
+ module Name
4
+ # Substitions
5
+ HYPHEN_START_REGEX = /^-+/.freeze
6
+ HYPEN_REGEX = /-+/.freeze
7
+ UNDERSCORE_REGEX = /_+/.freeze
8
+ SPACE_REGEX = /\s+/.freeze
9
+ # Checkers
10
+ OPTIONAL_REGEX = /\[\w+\]$/.freeze
11
+ SINGLE_HYPHEN_REGEX = /^-(?<options>[^- ][^ ]*)/.freeze
12
+ DOUBLE_HYPHEN_REGEX = /^--(?<option>[^- ][^ ]*)/.freeze
13
+
14
+ # @return [Boolean]
15
+ def single_hyphen?(value)
16
+ return false unless value.respond_to?(:to_s)
17
+ !!value.to_s.match(SINGLE_HYPHEN_REGEX)
18
+ end
19
+
20
+ # @return [Boolean]
21
+ def double_hyphen?(value)
22
+ return false unless value.respond_to?(:to_s)
23
+ !!value.to_s.match(DOUBLE_HYPHEN_REGEX)
24
+ end
25
+
26
+ # @param strict [Boolean] whether hyphen is required when declaring an option `short`
27
+ # @return [Boolean]
28
+ def valid_short?(value, strict: false)
29
+ return false unless value.respond_to?(:to_s) && !value.to_s.empty?
30
+ return false unless !strict || single_hypen(value)
31
+ short_sym(value).to_s.length == 1
32
+ end
33
+
34
+ # @param strict [Boolean] whether hyphen is required when declaring an option `name`
35
+ # @return [Boolean]
36
+ def valid_name?(value, strict: false)
37
+ return false unless value.respond_to?(:to_s) && !value.to_s.empty?
38
+ return false unless !strict || double_hyphen?(value)
39
+ name_sym(value).to_s.length > 1
40
+ end
41
+
42
+ # @param strict [Boolean] whether hyphen is required when declaring an option `short`
43
+ # @return [Boolean] whether `value` is an hyphened option `short`
44
+ def short_hyphen?(value, strict: false)
45
+ short?(value, strict: strict) && single_hypen(value)
46
+ end
47
+
48
+ # @param strict [Boolean] whether hyphen is required when declaring an option `name`
49
+ # @return [Boolean] whether `value` is an hyphened option `name`
50
+ def name_hyphen?(value, strict: false)
51
+ name?(value, strict: strict) && double_hyphen?(value)
52
+ end
53
+
54
+ # Converter
55
+ # @example
56
+ # * `"-d"` becomes `:d`
57
+ # @return [Symbol, NilClass]
58
+ def short_sym(value)
59
+ return nil unless value
60
+ value = value.to_s.gsub(HYPHEN_START_REGEX, '')
61
+ return nil unless value = value.chars.first
62
+ value.to_sym
63
+ end
64
+
65
+ # Converter.
66
+ # @example
67
+ # * `"--there-we-go ARGUMENT"` becomes `:"there_we_go ARGUMENT"`
68
+ # @note
69
+ # 1. It removes the double hyphen start (`--`)
70
+ # 2. Replaces any intermediate hyphen by underscore `_`
71
+ # 3. Replaces any multi-spacing by single space ` `
72
+ # @return [Symbol, NilClass]
73
+ def name_sym(value)
74
+ return nil unless value
75
+ value = value.to_s.gsub(HYPHEN_START_REGEX, '')
76
+ value = value.gsub(HYPEN_REGEX, '_')
77
+ value = value.gsub(SPACE_REGEX, ' ')
78
+ return nil if value.empty?
79
+ value.to_sym
80
+ end
81
+
82
+ # It's like `#name_sym` but it only gets the option name.
83
+ # @example
84
+ # * `"--there-we-go ARGUMENT"` becomes `:there_we_go`
85
+ # @see #name_sym
86
+ # @return [Symbol, NilClass]
87
+ def name_word_sym(value)
88
+ return nil unless value = name_sym(value)
89
+ return nil unless value = name_words(value).first
90
+ value.to_sym
91
+ end
92
+
93
+ # @return [String, NilClass] it returns the hyphened (`-`) version of a short `value`
94
+ def short_hyphen(value)
95
+ return nil unless value = short_sym(value)
96
+ "-#{value}"
97
+ end
98
+
99
+ # Gets the actual name of the option. First word.
100
+ # @example
101
+ # * `"--there-we-go ARGUMENT"` becomes `"--there-we-go"`
102
+ # * `"there-we-go"` becomes `"--there-we-go"`
103
+ # * `:there_we_go` becomes `"--there-we-go"`
104
+ # @return [String, NilClass] option `name` alone double hypened (`--`)
105
+ def name_hyphen(value)
106
+ return nil unless value = name_sym(value)
107
+ value = value.to_s.gsub(UNDERSCORE_REGEX, '-')
108
+ return nil if value.empty?
109
+ "--#{value}"
110
+ end
111
+
112
+ # @example
113
+ # * `"--there-we-go ARGUMENT"` returns `"ARGUMENT"`
114
+ # @return [String, NilClass] the argument of `value`, if present
115
+ def name_argument(value)
116
+ name_words(value)[1]
117
+ end
118
+
119
+ # @example
120
+ # * `"--there-we-go ARGUMENT"` returns `true`
121
+ # * `"--time"` returns `false`
122
+ # @return [String, NilClass] whether `value` is a name with argument
123
+ def name_argument?(value)
124
+ !!name_argument(value)
125
+ end
126
+
127
+ # @example
128
+ # * `"--there-we-go [ARGUMENT]"` returns `false`
129
+ # * `"--folder FOLDER"` returns `true`
130
+ # * `"--time"` returns `false`
131
+ # @return [Boolean] `true` if `value` does NOT end with `[String]`
132
+ def argument_required?(value)
133
+ return false unless value
134
+ !argument_optional?(value)
135
+ end
136
+
137
+ # @example
138
+ # * `"--there-we-go [ARGUMENT]"` returns `true`
139
+ # * `"--folder FOLDER"` returns `false`
140
+ # * `"--time"` returns `true`
141
+ # @note when there is NO argument it evaluates `true`
142
+ # @return [Boolean] `true` if `value` ends with `[String]`
143
+ def argument_optional?(value)
144
+ return true unless value
145
+ !!value.match(OPTIONAL_REGEX)
146
+ end
147
+
148
+ private
149
+
150
+ # @example
151
+ # * `"--there-we-go [ARGUMENT]"` returns `["there-we-go","[ARGUMENT]"]`
152
+ # @return [Array<String>] the words of `value` without hyphen start
153
+ def name_words(value)
154
+ return nil unless value
155
+ value = value.to_s.gsub(HYPHEN_START_REGEX, '')
156
+ value.to_s.split(SPACE_REGEX)
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,16 @@
1
+ class RakeCommander
2
+ module Options
3
+ class Set
4
+ include RakeCommander::Options
5
+
6
+ class << self
7
+ include Enumerable
8
+
9
+ def each(&block)
10
+ return to_enum(:each) unless block
11
+ options.values.each(&block)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,171 @@
1
+ require_relative 'options/name'
2
+ require_relative 'options/arguments'
3
+ require_relative 'option'
4
+
5
+ class RakeCommander
6
+ module Options
7
+ class << self
8
+ def included(base)
9
+ super(base)
10
+ base.extend RakeCommander::Base::ClassHelpers
11
+ base.extend ClassMethods
12
+ base.inheritable_attrs :banner, :options_hash, :results_with_all_defaults
13
+ base.extend RakeCommander::Options::Arguments
14
+ end
15
+ end
16
+
17
+ module ClassMethods
18
+ def options_hash
19
+ @options_hash ||= {}
20
+ end
21
+
22
+ def options
23
+ options_hash.values.uniq
24
+ end
25
+
26
+ def options?
27
+ !options.empty?
28
+ end
29
+
30
+ def clear_options!
31
+ @options_hash = {}
32
+ end
33
+
34
+ # Allows to use a set of options
35
+ # @param options [Enumerable<RakeCommander::Option>]
36
+ def use_options(options)
37
+ options = options.values if options.is_a?(Hash)
38
+ options.each do |opt|
39
+ add_to_options(opt)
40
+ end
41
+ self
42
+ end
43
+
44
+ # Defines a new option
45
+ # @note
46
+ # - It will override with a Warning options with same `short` or `name`
47
+ def option(*args, **kargs, &block)
48
+ opt = RakeCommander::Option.new(*args, **kargs, &block)
49
+ add_to_options(opt)
50
+ self
51
+ end
52
+
53
+ # Overrides the auto-generated banner
54
+ def banner(desc = :not_used)
55
+ return @banner = desc unless desc == :not_used
56
+ return @banner if @banner
57
+ return task_options_banner if respond_to?(:task_options_banner, true)
58
+ end
59
+
60
+ # @return [Boolean] whether results should include options defined
61
+ # with a default, regarless if they are invoked
62
+ def results_with_all_defaults(value = nil)
63
+ if value.nil?
64
+ @results_with_all_defaults || false
65
+ else
66
+ @results_with_all_defaults = !!value
67
+ end
68
+ end
69
+
70
+ # It builds the `OptionParser` injecting the `middleware` block.
71
+ # @return [Hash] with `short` option as `key` and final value as `value`.
72
+ def parse_options(argv = ARGV, leftovers: [], &middleware)
73
+ left_overs = []
74
+ options_parser_with_results(middleware) do |options_parser|
75
+ argv = pre_parse_arguments(argv, options_hash)
76
+ leftovers.push(*options_parser.parse(argv))
77
+ rescue OptionParser::MissingArgument => e
78
+ raise RakeCommander::Options::MissingArgument, e, cause: nil
79
+ rescue OptionParser::InvalidArgument => e
80
+ error = RakeCommander::Options::InvalidArgument
81
+ error = error.new(e)
82
+ if (opt = options_hash[error.option_sym]) && opt.argument_required?
83
+ msg = "missing required argument: #{opt.name_hyphen} (#{opt.short_hyphen})"
84
+ raise RakeCommander::Options::MissingArgument, msg, cause: nil
85
+ else
86
+ raise error, e, cause: nil
87
+ end
88
+ end.tap do |results|
89
+ check_required_presence!(results)
90
+ end
91
+ end
92
+
93
+ protected
94
+
95
+ # @return [OptionParser] the built options parser.
96
+ def options_parser(&middleware)
97
+ new_options_parser do |opts|
98
+ opts.banner = banner if banner
99
+ options.each {|opt| opt.add_switch(opts, &middleware)}
100
+ end
101
+ end
102
+
103
+ def new_options_parser(&block)
104
+ require 'optparse'
105
+ OptionParser.new(&block)
106
+ end
107
+
108
+ private
109
+
110
+ # Expects a block that should do the final call to `#parse`
111
+ def options_parser_with_results(middleware)
112
+ result_defaults.tap do |result|
113
+ results_collector = proc do |value, default, short, name|
114
+ middleware&.call(value, default, short, name)
115
+ result[short] = value.nil?? default : value
116
+ end
117
+ options_parser = options_parser(&results_collector)
118
+ yield(options_parser)
119
+ end
120
+ end
121
+
122
+ # Based on `required` options, it sets the `default`
123
+ def result_defaults
124
+ {}.tap do |res_def|
125
+ options.select do |opt|
126
+ (results_with_all_defaults && opt.default?) \
127
+ || (opt.required? && opt.default?)
128
+ end.each do |opt|
129
+ res_def[opt.short] = opt.default
130
+ end
131
+ end
132
+ end
133
+
134
+ # It throws an exception if any of the required options
135
+ # is missing in results
136
+ def check_required_presence!(results)
137
+ missing = options.select do |opt|
138
+ opt.required?
139
+ end.reject do |opt|
140
+ results.key?(opt.short) || results.key?(opt.name)
141
+ end
142
+ raise RakeCommander::Options::MissingOption.new(missing) unless missing.empty?
143
+ end
144
+
145
+ def add_to_options(opt)
146
+ if prev = options_hash[opt.short]
147
+ puts "Warning: Overriding option with short '#{prev.short}' ('#{prev.name}')"
148
+ options_hash.delete(prev.short)
149
+ options_hash.delete(prev.name)
150
+ end
151
+ if prev = options_hash[opt.name]
152
+ puts "Warning: Overriding option with name '#{prev.name}' ('#{prev.short}')"
153
+ options_hash.delete(prev.short)
154
+ options_hash.delete(prev.name)
155
+ end
156
+ options_hash[opt.name] = options_hash[opt.short] = opt
157
+ end
158
+ end
159
+
160
+ def options(argv = ARGV)
161
+ @options ||= self.class.parse_options(argv, leftovers: self.options_leftovers)
162
+ end
163
+
164
+ def options_leftovers
165
+ @options_leftovers ||= []
166
+ end
167
+ end
168
+ end
169
+
170
+ require_relative 'options/error'
171
+ require_relative 'options/set'