rake-commander 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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'