boson 0.4.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gemspec +6 -7
- data/.rspec +2 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.rdoc +1 -1
- data/README.md +144 -0
- data/README.rdoc +2 -2
- data/Upgrading.md +23 -0
- data/bin/boson +2 -2
- data/lib/boson.rb +44 -52
- data/lib/boson/bare_runner.rb +83 -0
- data/lib/boson/bin_runner.rb +114 -0
- data/lib/boson/command.rb +92 -132
- data/lib/boson/inspector.rb +49 -48
- data/lib/boson/library.rb +71 -120
- data/lib/boson/loader.rb +73 -84
- data/lib/boson/manager.rb +131 -135
- data/lib/boson/method_inspector.rb +112 -0
- data/lib/boson/option_command.rb +71 -154
- data/lib/boson/option_parser.rb +178 -173
- data/lib/boson/options.rb +46 -32
- data/lib/boson/runner.rb +58 -66
- data/lib/boson/runner_library.rb +31 -0
- data/lib/boson/scientist.rb +48 -81
- data/lib/boson/util.rb +46 -61
- data/lib/boson/version.rb +1 -1
- data/test/bin_runner_test.rb +53 -191
- data/test/command_test.rb +5 -9
- data/test/deps.rip +2 -2
- data/test/loader_test.rb +18 -216
- data/test/manager_test.rb +69 -79
- data/test/method_inspector_test.rb +12 -36
- data/test/option_parser_test.rb +45 -32
- data/test/runner_library_test.rb +10 -0
- data/test/runner_test.rb +158 -28
- data/test/scientist_test.rb +9 -147
- data/test/test_helper.rb +87 -52
- metadata +30 -72
- data/deps.rip +0 -2
- data/lib/boson/commands.rb +0 -7
- data/lib/boson/commands/core.rb +0 -77
- data/lib/boson/commands/web_core.rb +0 -153
- data/lib/boson/index.rb +0 -48
- data/lib/boson/inspectors/argument_inspector.rb +0 -97
- data/lib/boson/inspectors/comment_inspector.rb +0 -100
- data/lib/boson/inspectors/method_inspector.rb +0 -98
- data/lib/boson/libraries/file_library.rb +0 -144
- data/lib/boson/libraries/gem_library.rb +0 -30
- data/lib/boson/libraries/local_file_library.rb +0 -30
- data/lib/boson/libraries/module_library.rb +0 -37
- data/lib/boson/libraries/require_library.rb +0 -23
- data/lib/boson/namespace.rb +0 -31
- data/lib/boson/pipe.rb +0 -147
- data/lib/boson/pipes.rb +0 -75
- data/lib/boson/repo.rb +0 -107
- data/lib/boson/runners/bin_runner.rb +0 -208
- data/lib/boson/runners/console_runner.rb +0 -58
- data/lib/boson/view.rb +0 -95
- data/test/argument_inspector_test.rb +0 -62
- data/test/commands_test.rb +0 -22
- data/test/comment_inspector_test.rb +0 -126
- data/test/file_library_test.rb +0 -42
- data/test/pipes_test.rb +0 -65
- data/test/repo_index_test.rb +0 -122
- data/test/repo_test.rb +0 -23
@@ -0,0 +1,112 @@
|
|
1
|
+
module Boson
|
2
|
+
# Gathers method attributes by redefining method_added and capturing method
|
3
|
+
# calls before a method.
|
4
|
+
class MethodInspector
|
5
|
+
METHODS = [:config, :desc, :options]
|
6
|
+
SCRAPEABLE_METHODS = [:options]
|
7
|
+
METHOD_CLASSES = {:config=>Hash, :desc=>String, :options=>Hash}
|
8
|
+
ALL_METHODS = METHODS + [:option]
|
9
|
+
|
10
|
+
def self.safe_new_method_added(mod, meth)
|
11
|
+
return unless mod.to_s[/^Boson::Commands::/]
|
12
|
+
new_method_added(mod, meth)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.new_method_added(mod, meth)
|
16
|
+
instance.new_method_added(mod, meth)
|
17
|
+
end
|
18
|
+
|
19
|
+
class << self; attr_accessor :instance end
|
20
|
+
|
21
|
+
def self.instance
|
22
|
+
@instance ||= new
|
23
|
+
end
|
24
|
+
|
25
|
+
(METHODS + [:option, :mod_store]).each do |meth|
|
26
|
+
define_singleton_method(meth) do |*args|
|
27
|
+
instance.send(meth, *args)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_accessor :current_module, :mod_store
|
32
|
+
def initialize
|
33
|
+
@mod_store = {}
|
34
|
+
end
|
35
|
+
|
36
|
+
# The method_added used while scraping method attributes.
|
37
|
+
def new_method_added(mod, meth)
|
38
|
+
self.current_module = mod
|
39
|
+
|
40
|
+
store[:temp] ||= {}
|
41
|
+
METHODS.each do |e|
|
42
|
+
store[e][meth.to_s] = store[:temp][e] if store[:temp][e]
|
43
|
+
end
|
44
|
+
if store[:temp][:option]
|
45
|
+
(store[:options][meth.to_s] ||= {}).merge! store[:temp][:option]
|
46
|
+
end
|
47
|
+
during_new_method_added mod, meth
|
48
|
+
store[:temp] = {}
|
49
|
+
|
50
|
+
if SCRAPEABLE_METHODS.any? {|m| has_inspector_method?(meth, m) }
|
51
|
+
set_arguments(mod, meth)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
METHODS.each do |e|
|
56
|
+
define_method(e) do |mod, val|
|
57
|
+
(@mod_store[mod] ||= {})[e] ||= {}
|
58
|
+
(store(mod)[:temp] ||= {})[e] = val
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Scrapes option
|
63
|
+
def option(mod, name, value)
|
64
|
+
(@mod_store[mod] ||= {})[:options] ||= {}
|
65
|
+
(store(mod)[:temp] ||= {})[:option] ||= {}
|
66
|
+
(store(mod)[:temp] ||= {})[:option][name] = value
|
67
|
+
end
|
68
|
+
|
69
|
+
# Hash of a module's method attributes i.e. descriptions, options by method
|
70
|
+
# and then attribute
|
71
|
+
def store(mod=@current_module)
|
72
|
+
@mod_store[mod]
|
73
|
+
end
|
74
|
+
|
75
|
+
# Renames store key from old to new name
|
76
|
+
def rename_store_key(old, new)
|
77
|
+
mod_store[new] = mod_store.delete old
|
78
|
+
end
|
79
|
+
|
80
|
+
# Sets current module
|
81
|
+
def current_module=(mod)
|
82
|
+
@current_module = mod
|
83
|
+
@mod_store[mod] ||= {}
|
84
|
+
end
|
85
|
+
|
86
|
+
module API
|
87
|
+
# Method hook called during new_method_added
|
88
|
+
def during_new_method_added(mod, meth); end
|
89
|
+
|
90
|
+
def set_arguments(mod, meth)
|
91
|
+
store[:args] ||= {}
|
92
|
+
|
93
|
+
args = mod.instance_method(meth).parameters.map do|(type, name)|
|
94
|
+
case type
|
95
|
+
when :rest then ["*#{name}"]
|
96
|
+
when :req then [name.to_s]
|
97
|
+
when :opt then [name.to_s, '']
|
98
|
+
else nil
|
99
|
+
end
|
100
|
+
end.compact
|
101
|
+
|
102
|
+
store[:args][meth.to_s] = args
|
103
|
+
end
|
104
|
+
|
105
|
+
# Determines if method's arguments should be scraped
|
106
|
+
def has_inspector_method?(meth, inspector)
|
107
|
+
true
|
108
|
+
end
|
109
|
+
end
|
110
|
+
include API
|
111
|
+
end
|
112
|
+
end
|
data/lib/boson/option_command.rb
CHANGED
@@ -1,105 +1,36 @@
|
|
1
1
|
require 'shellwords'
|
2
2
|
module Boson
|
3
|
-
# A class used by Scientist to wrap around Command objects. It's main purpose
|
4
|
-
# a command's global
|
5
|
-
#
|
6
|
-
# When passing options to commands, global ones _must_
|
7
|
-
# Also, options _must_ all be passed either
|
8
|
-
#
|
3
|
+
# A class used by Scientist to wrap around Command objects. It's main purpose
|
4
|
+
# is to parse a command's global and local options. As the names imply,
|
5
|
+
# global options are available to all commands while local options are
|
6
|
+
# specific to a command. When passing options to commands, global ones _must_
|
7
|
+
# be passed first, then local ones. Also, options _must_ all be passed either
|
8
|
+
# before or after arguments.
|
9
9
|
#
|
10
10
|
# === Basic Global Options
|
11
|
-
# Any command with options comes with basic global options. For example '-
|
12
|
-
#
|
13
|
-
# option
|
14
|
-
# command if executed. For example:
|
15
|
-
#
|
16
|
-
# # Define this command in a library
|
17
|
-
# options :level=>:numeric, :verbose=>:boolean
|
18
|
-
# def foo(*args)
|
19
|
-
# args
|
20
|
-
# end
|
21
|
-
#
|
22
|
-
# irb>> foo 'testin -p -l=1'
|
23
|
-
# Arguments: ["testin", {:level=>1}]
|
24
|
-
# Global options: {:pretend=>true}
|
25
|
-
#
|
26
|
-
# If a global option conflicts with a local option, the local option takes precedence. You can get around
|
27
|
-
# this by passing global options after a '-'. For example, if the global option -f (--fields) conflicts with
|
28
|
-
# a local -f (--force):
|
29
|
-
# foo 'arg1 -v -f - -f=f1,f2'
|
30
|
-
# # is the same as
|
31
|
-
# foo 'arg1 -v --fields=f1,f2 -f'
|
32
|
-
#
|
33
|
-
# === Toggling Views With the Basic Global Option --render
|
34
|
-
# One of the more important global options is --render. This option toggles the rendering of a command's
|
35
|
-
# output done with View and Hirb[http://github.com/cldwalker/hirb].
|
36
|
-
#
|
37
|
-
# Here's a simple example of toggling Hirb's table view:
|
38
|
-
# # Defined in a library file:
|
39
|
-
# #@options {}
|
40
|
-
# def list(options={})
|
41
|
-
# [1,2,3]
|
42
|
-
# end
|
43
|
-
#
|
44
|
-
# Using it in irb:
|
45
|
-
# >> list
|
46
|
-
# => [1,2,3]
|
47
|
-
# >> list '-r' # or list --render
|
48
|
-
# +-------+
|
49
|
-
# | value |
|
50
|
-
# +-------+
|
51
|
-
# | 1 |
|
52
|
-
# | 2 |
|
53
|
-
# | 3 |
|
54
|
-
# +-------+
|
55
|
-
# 3 rows in set
|
56
|
-
# => true
|
11
|
+
# Any command with options comes with basic global options. For example '-h'
|
12
|
+
# on an option command prints help. If a global option conflicts with a local
|
13
|
+
# option, the local option takes precedence.
|
57
14
|
class OptionCommand
|
58
15
|
# ArgumentError specific to @command's arguments
|
59
16
|
class CommandArgumentError < ::ArgumentError; end
|
60
17
|
|
18
|
+
# default global options
|
61
19
|
BASIC_OPTIONS = {
|
62
20
|
:help=>{:type=>:boolean, :desc=>"Display a command's help"},
|
63
|
-
|
64
|
-
:verbose=>{:type=>:boolean, :desc=>"Increase verbosity for help, errors, etc."},
|
65
|
-
:usage_options=>{:type=>:string, :desc=>"Render options to pass to usage/help"},
|
66
|
-
:pretend=>{:type=>:boolean, :desc=>"Display what a command would execute without executing it"},
|
67
|
-
:delete_options=>{:type=>:array, :desc=>'Deletes global options starting with given strings' }
|
68
|
-
} #:nodoc:
|
69
|
-
|
70
|
-
RENDER_OPTIONS = {
|
71
|
-
:fields=>{:type=>:array, :desc=>"Displays fields in the order given"},
|
72
|
-
:class=>{:type=>:string, :desc=>"Hirb helper class which renders"},
|
73
|
-
:max_width=>{:type=>:numeric, :desc=>"Max width of a table"},
|
74
|
-
:vertical=>{:type=>:boolean, :desc=>"Display a vertical table"},
|
75
|
-
} #:nodoc:
|
76
|
-
|
77
|
-
PIPE_OPTIONS = {
|
78
|
-
:sort=>{:type=>:string, :desc=>"Sort by given field"},
|
79
|
-
:reverse_sort=>{:type=>:boolean, :desc=>"Reverse a given sort"},
|
80
|
-
:query=>{:type=>:hash, :desc=>"Queries fields given field:search pairs"},
|
81
|
-
:pipes=>{:alias=>'P', :type=>:array, :desc=>"Pipe to commands sequentially"}
|
82
|
-
} #:nodoc:
|
21
|
+
}
|
83
22
|
|
84
23
|
class <<self
|
85
|
-
#:stopdoc:
|
86
24
|
def default_option_parser
|
87
|
-
@default_option_parser ||= OptionParser.new
|
88
|
-
merge(default_render_options.merge(BASIC_OPTIONS))
|
25
|
+
@default_option_parser ||= OptionParser.new default_options
|
89
26
|
end
|
90
27
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
def default_render_options
|
96
|
-
@default_render_options ||= RENDER_OPTIONS.merge Boson.repo.config[:render_options] || {}
|
97
|
-
end
|
98
|
-
|
99
|
-
def delete_non_render_options(opt)
|
100
|
-
opt.delete_if {|k,v| BASIC_OPTIONS.keys.include?(k) }
|
28
|
+
module API
|
29
|
+
def default_options
|
30
|
+
BASIC_OPTIONS
|
31
|
+
end
|
101
32
|
end
|
102
|
-
|
33
|
+
include API
|
103
34
|
end
|
104
35
|
|
105
36
|
attr_accessor :command
|
@@ -107,102 +38,63 @@ module Boson
|
|
107
38
|
@command = cmd
|
108
39
|
end
|
109
40
|
|
110
|
-
# Parses arguments and returns global options, local options and leftover
|
41
|
+
# Parses arguments and returns global options, local options and leftover
|
42
|
+
# arguments.
|
111
43
|
def parse(args)
|
112
44
|
if args.size == 1 && args[0].is_a?(String)
|
113
45
|
global_opt, parsed_options, args = parse_options Shellwords.shellwords(args[0])
|
114
46
|
# last string argument interpreted as args + options
|
115
47
|
elsif args.size > 1 && args[-1].is_a?(String)
|
116
|
-
temp_args =
|
48
|
+
temp_args = Boson.in_shell ? args : Shellwords.shellwords(args.pop)
|
117
49
|
global_opt, parsed_options, new_args = parse_options temp_args
|
118
|
-
|
50
|
+
Boson.in_shell ? args = new_args : args += new_args
|
119
51
|
# add default options
|
120
|
-
elsif @command.options.nil? || @command.options.empty? ||
|
121
|
-
args.size <= (@command.arg_size - 1).abs) ||
|
52
|
+
elsif @command.options.nil? || @command.options.empty? ||
|
53
|
+
(!@command.has_splat_args? && args.size <= (@command.arg_size - 1).abs) ||
|
54
|
+
(@command.has_splat_args? && !args[-1].is_a?(Hash))
|
122
55
|
global_opt, parsed_options = parse_options([])[0,2]
|
123
56
|
# merge default options with given hash of options
|
124
|
-
elsif (@command.has_splat_args? || (args.size == @command.arg_size)) &&
|
125
|
-
|
126
|
-
|
57
|
+
elsif (@command.has_splat_args? || (args.size == @command.arg_size)) &&
|
58
|
+
args[-1].is_a?(Hash)
|
59
|
+
global_opt, parsed_options = parse_options([])[0,2]
|
60
|
+
parsed_options.merge!(args.pop)
|
127
61
|
end
|
128
62
|
[global_opt || {}, parsed_options, args]
|
129
63
|
end
|
130
64
|
|
131
|
-
#:stopdoc:
|
132
|
-
def parse_options(args)
|
133
|
-
parsed_options = @command.option_parser.parse(args, :delete_invalid_opts=>true)
|
134
|
-
trailing, unparseable = split_trailing
|
135
|
-
global_options = parse_global_options @command.option_parser.leading_non_opts + trailing
|
136
|
-
new_args = option_parser.non_opts.dup + unparseable
|
137
|
-
[global_options, parsed_options, new_args]
|
138
|
-
rescue OptionParser::Error
|
139
|
-
global_options = parse_global_options @command.option_parser.leading_non_opts + split_trailing[0]
|
140
|
-
global_options[:help] ? [global_options, nil, []] : raise
|
141
|
-
end
|
142
|
-
|
143
|
-
def split_trailing
|
144
|
-
trailing = @command.option_parser.trailing_non_opts
|
145
|
-
if trailing[0] == '--'
|
146
|
-
trailing.shift
|
147
|
-
[ [], trailing ]
|
148
|
-
else
|
149
|
-
trailing.shift if trailing[0] == '-'
|
150
|
-
[ trailing, [] ]
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
65
|
def parse_global_options(args)
|
155
66
|
option_parser.parse args
|
156
67
|
end
|
157
68
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
def all_global_options
|
164
|
-
OptionParser.make_mergeable! @command.render_options
|
165
|
-
render_opts = Util.recursive_hash_merge(@command.render_options, Util.deep_copy(self.class.default_render_options))
|
166
|
-
merged_opts = Util.recursive_hash_merge Util.deep_copy(self.class.default_pipe_options), render_opts
|
167
|
-
opts = Util.recursive_hash_merge merged_opts, Util.deep_copy(BASIC_OPTIONS)
|
168
|
-
set_global_option_defaults opts
|
169
|
-
end
|
170
|
-
|
171
|
-
def set_global_option_defaults(opts)
|
172
|
-
if !opts[:fields].key?(:values)
|
173
|
-
if opts[:fields][:default]
|
174
|
-
opts[:fields][:values] = opts[:fields][:default]
|
175
|
-
else
|
176
|
-
if opts[:change_fields] && (changed = opts[:change_fields][:default])
|
177
|
-
opts[:fields][:values] = changed.is_a?(Array) ? changed : changed.values
|
178
|
-
end
|
179
|
-
opts[:fields][:values] ||= opts[:headers][:default].keys if opts[:headers] && opts[:headers][:default]
|
180
|
-
end
|
181
|
-
opts[:fields][:enum] = false if opts[:fields][:values] && !opts[:fields].key?(:enum)
|
182
|
-
end
|
183
|
-
if opts[:fields][:values]
|
184
|
-
opts[:sort][:values] ||= opts[:fields][:values]
|
185
|
-
opts[:query][:keys] ||= opts[:fields][:values]
|
186
|
-
opts[:query][:default_keys] ||= "*"
|
69
|
+
module API
|
70
|
+
# option parser just for option command
|
71
|
+
def option_parser
|
72
|
+
@option_parser ||= self.class.default_option_parser
|
187
73
|
end
|
188
|
-
opts
|
189
74
|
end
|
75
|
+
include API
|
190
76
|
|
77
|
+
# modifies args for edge cases
|
191
78
|
def modify_args(args)
|
192
|
-
if @command.default_option && @command.arg_size <= 1 &&
|
79
|
+
if @command.default_option && @command.arg_size <= 1 &&
|
80
|
+
!@command.has_splat_args? &&
|
193
81
|
!args[0].is_a?(Hash) && args[0].to_s[/./] != '-' && !args.join.empty?
|
194
|
-
|
82
|
+
args[0] = "--#{@command.default_option}=#{args[0]}"
|
195
83
|
end
|
196
84
|
end
|
197
85
|
|
86
|
+
# raises CommandArgumentError if argument size is incorrect for given args
|
198
87
|
def check_argument_size(args)
|
199
88
|
if args.size != @command.arg_size && !@command.has_splat_args?
|
200
|
-
command_size, args_size = args.size > @command.arg_size ?
|
89
|
+
command_size, args_size = args.size > @command.arg_size ?
|
90
|
+
[@command.arg_size, args.size] :
|
201
91
|
[@command.arg_size - 1, args.size - 1]
|
202
|
-
raise CommandArgumentError,
|
92
|
+
raise CommandArgumentError,
|
93
|
+
"wrong number of arguments (#{args_size} for #{command_size})"
|
203
94
|
end
|
204
95
|
end
|
205
96
|
|
97
|
+
# Adds default args as original method would
|
206
98
|
def add_default_args(args, obj)
|
207
99
|
if @command.args && args.size < @command.args.size - 1
|
208
100
|
# leave off last arg since its an option
|
@@ -210,13 +102,38 @@ module Boson
|
|
210
102
|
next if args.size >= i + 1 # only fill in once args run out
|
211
103
|
break if arr.size != 2 # a default arg value must exist
|
212
104
|
begin
|
213
|
-
args[i] = @command.file_parsed_args
|
105
|
+
args[i] = @command.file_parsed_args ? obj.instance_eval(arr[1]) : arr[1]
|
214
106
|
rescue Exception
|
215
|
-
raise Scientist::Error, "Unable to set default argument at
|
107
|
+
raise Scientist::Error, "Unable to set default argument at " +
|
108
|
+
"position #{i+1}.\nReason: #{$!.message}"
|
216
109
|
end
|
217
110
|
}
|
218
111
|
end
|
219
112
|
end
|
220
|
-
|
113
|
+
|
114
|
+
private
|
115
|
+
def parse_options(args)
|
116
|
+
parsed_options = @command.option_parser.parse(args, delete_invalid_opts: true)
|
117
|
+
trailing, unparseable = split_trailing
|
118
|
+
global_options = parse_global_options @command.option_parser.leading_non_opts +
|
119
|
+
trailing
|
120
|
+
new_args = option_parser.non_opts.dup + unparseable
|
121
|
+
[global_options, parsed_options, new_args]
|
122
|
+
rescue OptionParser::Error
|
123
|
+
global_options = parse_global_options @command.option_parser.leading_non_opts +
|
124
|
+
split_trailing[0]
|
125
|
+
global_options[:help] ? [global_options, nil, []] : raise
|
126
|
+
end
|
127
|
+
|
128
|
+
def split_trailing
|
129
|
+
trailing = @command.option_parser.trailing_non_opts
|
130
|
+
if trailing[0] == '--'
|
131
|
+
trailing.shift
|
132
|
+
[ [], trailing ]
|
133
|
+
else
|
134
|
+
trailing.shift if trailing[0] == '-'
|
135
|
+
[ trailing, [] ]
|
136
|
+
end
|
137
|
+
end
|
221
138
|
end
|
222
139
|
end
|
data/lib/boson/option_parser.rb
CHANGED
@@ -1,47 +1,24 @@
|
|
1
1
|
module Boson
|
2
|
-
#
|
3
|
-
#
|
4
|
-
class IndifferentAccessHash < ::Hash
|
5
|
-
#:stopdoc:
|
6
|
-
def initialize(hash={})
|
7
|
-
super()
|
8
|
-
hash.each {|k,v| self[k] = v }
|
9
|
-
end
|
10
|
-
|
11
|
-
def [](key)
|
12
|
-
super convert_key(key)
|
13
|
-
end
|
14
|
-
|
15
|
-
def []=(key, value)
|
16
|
-
super convert_key(key), value
|
17
|
-
end
|
18
|
-
|
19
|
-
def values_at(*indices)
|
20
|
-
indices.collect { |key| self[convert_key(key)] }
|
21
|
-
end
|
22
|
-
|
23
|
-
protected
|
24
|
-
def convert_key(key)
|
25
|
-
key.kind_of?(String) ? key.to_sym : key
|
26
|
-
end
|
27
|
-
#:startdoc:
|
28
|
-
end
|
29
|
-
|
30
|
-
# This class concisely defines commandline options that when parsed produce a Hash of option keys and values.
|
2
|
+
# This class concisely defines commandline options that when parsed produce a
|
3
|
+
# Hash of option keys and values.
|
31
4
|
# Additional points:
|
32
|
-
# * Setting option values should follow conventions in *nix environments.
|
33
|
-
#
|
34
|
-
# *
|
35
|
-
#
|
36
|
-
# *
|
37
|
-
#
|
38
|
-
# *
|
5
|
+
# * Setting option values should follow conventions in *nix environments.
|
6
|
+
# See examples below.
|
7
|
+
# * By default, there are 5 option types, each which produce different
|
8
|
+
# objects for option values.
|
9
|
+
# * The default option types can produce objects for one or more of the following
|
10
|
+
# Ruby classes: String, Integer, Float, Array, Hash, FalseClass, TrueClass.
|
11
|
+
# * Users can define their own option types which create objects for _any_
|
12
|
+
# Ruby class. See Options.
|
13
|
+
# * Each option type can have attributes to enable more features (see
|
14
|
+
# OptionParser.new).
|
15
|
+
# * When options are parsed by parse(), an indifferent access hash is returned.
|
39
16
|
# * Options are also called switches, parameters, flags etc.
|
40
17
|
# * Option parsing stops when it comes across a '--'.
|
41
18
|
#
|
42
19
|
# Default option types:
|
43
|
-
# [*:boolean*] This option has no passed value. To toogle a boolean, prepend
|
44
|
-
# Multiple booleans can be joined together.
|
20
|
+
# [*:boolean*] This option has no passed value. To toogle a boolean, prepend
|
21
|
+
# with '--no-'. Multiple booleans can be joined together.
|
45
22
|
# '--debug' -> {:debug=>true}
|
46
23
|
# '--no-debug' -> {:debug=>false}
|
47
24
|
# '--no-d' -> {:debug=>false}
|
@@ -50,23 +27,25 @@ module Boson
|
|
50
27
|
# '--color red' -> {:color=>'red'}
|
51
28
|
# '--color=red' -> {:color=>'red'}
|
52
29
|
# '--color "gotta love spaces"' -> {:color=>'gotta love spaces'}
|
53
|
-
# [*:numeric*] Sets values as :string does or by appending number right after
|
54
|
-
# can be appended to joined booleans.
|
30
|
+
# [*:numeric*] Sets values as :string does or by appending number right after
|
31
|
+
# aliased name. Shortened form can be appended to joined booleans.
|
55
32
|
# '-n3' -> {:num=>3}
|
56
33
|
# '-dn3' -> {:debug=>true, :num=>3}
|
57
|
-
# [*:array*] Sets values as :string does. Multiple values are split by a
|
58
|
-
# Default is ',' (see OptionParser.new).
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
34
|
+
# [*:array*] Sets values as :string does. Multiple values are split by a
|
35
|
+
# configurable character Default is ',' (see OptionParser.new).
|
36
|
+
# Passing '*' refers to all known :values.
|
37
|
+
# '--fields 1,2,3' -> {:fields=>['1','2','3']}
|
38
|
+
# '--fields *' -> {:fields=>['1','2','3']}
|
39
|
+
# [*:hash*] Sets values as :string does. Key-value pairs are split by ':' and
|
40
|
+
# pairs are split by a configurable character (default ',').
|
41
|
+
# Multiple keys can be joined to one value. Passing '*' as a key
|
42
|
+
# refers to all known :keys.
|
64
43
|
# '--fields a:b,c:d' -> {:fields=>{'a'=>'b', 'c'=>'d'} }
|
65
44
|
# '--fields a,b:d' -> {:fields=>{'a'=>'d', 'b'=>'d'} }
|
66
45
|
# '--fields *:d' -> {:fields=>{'a'=>'d', 'b'=>'d', 'c'=>'d'} }
|
67
46
|
#
|
68
|
-
# This is a modified version of Yehuda Katz's Thor::Options class which is a
|
69
|
-
# of Daniel Berger's Getopt::Long class (
|
47
|
+
# This is a modified version of Yehuda Katz's Thor::Options class which is a
|
48
|
+
# modified version of Daniel Berger's Getopt::Long class (Ruby license).
|
70
49
|
class OptionParser
|
71
50
|
# Raised for all OptionParser errors
|
72
51
|
class Error < StandardError; end
|
@@ -75,42 +54,22 @@ module Boson
|
|
75
54
|
LONG_RE = /^(--\w+[-\w+]*)$/
|
76
55
|
SHORT_RE = /^(-[a-zA-Z])$/i
|
77
56
|
EQ_RE = /^(--\w+[-\w+]*|-[a-zA-Z])=(.*)$/i
|
78
|
-
|
57
|
+
# Allow either -x -v or -xv style for single char args
|
58
|
+
SHORT_SQ_RE = /^-([a-zA-Z]{2,})$/i
|
79
59
|
SHORT_NUM = /^(-[a-zA-Z])#{NUMERIC}$/i
|
80
60
|
STOP_STRINGS = %w{-- -}
|
81
|
-
|
82
|
-
attr_reader :leading_non_opts, :trailing_non_opts, :opt_aliases
|
83
|
-
|
84
|
-
# Given options to pass to OptionParser.new, this method parses ARGV and returns the remaining arguments
|
85
|
-
# and a hash of parsed options. This is useful for scripts outside of Boson.
|
86
|
-
def self.parse(options, args=ARGV)
|
87
|
-
@opt_parser ||= new(options)
|
88
|
-
parsed_options = @opt_parser.parse(args)
|
89
|
-
[@opt_parser.non_opts, parsed_options]
|
90
|
-
end
|
91
|
-
|
92
|
-
# Usage string summarizing options defined in parse
|
93
|
-
def self.usage
|
94
|
-
@opt_parser.to_s
|
95
|
-
end
|
96
61
|
|
97
|
-
|
98
|
-
opts.each {|k,v|
|
99
|
-
if !v.is_a?(Hash) && !v.is_a?(Symbol)
|
100
|
-
opts[k] = {:default=>v}
|
101
|
-
end
|
102
|
-
}
|
103
|
-
end
|
62
|
+
attr_reader :leading_non_opts, :trailing_non_opts, :opt_aliases
|
104
63
|
|
105
64
|
# Array of arguments left after defined options have been parsed out by parse.
|
106
65
|
def non_opts
|
107
66
|
leading_non_opts + trailing_non_opts
|
108
67
|
end
|
109
68
|
|
110
|
-
# Takes a hash of options. Each option, a key-value pair, must provide the
|
111
|
-
# name and type. Names longer than one character are accessed with
|
112
|
-
# one character names are accessed with '-'. Names can be
|
113
|
-
# or even dasherized strings:
|
69
|
+
# Takes a hash of options. Each option, a key-value pair, must provide the
|
70
|
+
# option's name and type. Names longer than one character are accessed with
|
71
|
+
# '--' while one character names are accessed with '-'. Names can be
|
72
|
+
# symbols, strings or even dasherized strings:
|
114
73
|
#
|
115
74
|
# Boson::OptionParser.new :debug=>:boolean, 'level'=>:numeric,
|
116
75
|
# '--fields'=>:array
|
@@ -121,47 +80,62 @@ module Boson
|
|
121
80
|
# Boson::OptionParser.new :debug=>true, 'level'=>3.1, :fields=>%w{f1 f2}
|
122
81
|
#
|
123
82
|
# By default every option name longer than one character is given an alias,
|
124
|
-
# the first character from its name. For example, the --fields option
|
125
|
-
#
|
83
|
+
# the first character from its name. For example, the --fields option has -f
|
84
|
+
# as its alias. You can override the default alias by providing your own
|
126
85
|
# option aliases as an array in the option's key.
|
127
86
|
#
|
128
87
|
# Boson::OptionParser.new [:debug, :damnit, :D]=>true
|
129
88
|
#
|
130
|
-
# Note that aliases are accessed the same way as option names. For the
|
131
|
-
# --debug, --damnit and -D all refer to the same option.
|
89
|
+
# Note that aliases are accessed the same way as option names. For the
|
90
|
+
# above, --debug, --damnit and -D all refer to the same option.
|
91
|
+
#
|
92
|
+
# Options can have additional attributes by passing a hash to the option
|
93
|
+
# value instead of a type or default:
|
132
94
|
#
|
133
|
-
# Options can have additional attributes by passing a hash to the option value instead of
|
134
|
-
# a type or default:
|
135
|
-
#
|
136
95
|
# Boson::OptionParser.new :fields=>{:type=>:array, :values=>%w{f1 f2 f3},
|
137
96
|
# :enum=>false}
|
138
97
|
#
|
139
|
-
# These attributes are available when an option is parsed via
|
140
|
-
# Here are the available option attributes for the
|
98
|
+
# These attributes are available when an option is parsed via
|
99
|
+
# current_attributes(). Here are the available option attributes for the
|
100
|
+
# default option types:
|
141
101
|
#
|
142
|
-
# [*:type*] This or :default is required. Available types are :string,
|
143
|
-
#
|
144
|
-
# [*:
|
145
|
-
#
|
146
|
-
#
|
147
|
-
#
|
148
|
-
#
|
149
|
-
#
|
150
|
-
#
|
151
|
-
#
|
152
|
-
# [*:
|
153
|
-
#
|
154
|
-
#
|
155
|
-
#
|
156
|
-
#
|
157
|
-
# [*:
|
158
|
-
#
|
159
|
-
#
|
160
|
-
#
|
161
|
-
#
|
162
|
-
#
|
163
|
-
#
|
164
|
-
#
|
102
|
+
# [*:type*] This or :default is required. Available types are :string,
|
103
|
+
# :boolean, :array, :numeric, :hash.
|
104
|
+
# [*:default*] This or :type is required. This is the default value an
|
105
|
+
# option has when not passed.
|
106
|
+
# [*:bool_default*] This is the value an option has when passed as a
|
107
|
+
# boolean. However, by enabling this an option can only
|
108
|
+
# have explicit values with '=' i.e. '--index=alias' and
|
109
|
+
# no '--index alias'. If this value is a string, it is
|
110
|
+
# parsed as any option value would be. Otherwise, the
|
111
|
+
# value is passed directly without parsing.
|
112
|
+
# [*:required*] Boolean indicating if option is required. Option parses
|
113
|
+
# raises error if value not given. Default is false.
|
114
|
+
# [*:alias*] Alternative way to define option aliases with an option name
|
115
|
+
# or an array of them. Useful in yaml files. Setting to false
|
116
|
+
# will prevent creating an automatic alias.
|
117
|
+
# [*:values*] An array of values an option can have. Available for :array
|
118
|
+
# and :string options. Values here can be aliased by typing a
|
119
|
+
# unique string it starts with or underscore aliasing (see
|
120
|
+
# Util.underscore_search). For example, for values foo, odd and
|
121
|
+
# obnoxiously_long, f refers to foo, od to odd and o_l to
|
122
|
+
# obnoxiously_long.
|
123
|
+
# [*:enum*] Boolean indicating if an option enforces values in :values or
|
124
|
+
# :keys. Default is true. For :array, :hash and :string options.
|
125
|
+
# [*:split*] For :array and :hash options. A string or regular expression
|
126
|
+
# on which an array value splits to produce an array of values.
|
127
|
+
# Default is ','.
|
128
|
+
# [*:keys*] :hash option only. An array of values a hash option's keys can
|
129
|
+
# have. Keys can be aliased just like :values.
|
130
|
+
# [*:default_keys*] For :hash option only. Default keys to assume when only
|
131
|
+
# a value is given. Multiple keys can be joined by the
|
132
|
+
# :split character. Defaults to first key of :keys if
|
133
|
+
# :keys given.
|
134
|
+
# [*:regexp*] For :array option with a :values attribute. Boolean indicating
|
135
|
+
# that each option value does a regular expression search of
|
136
|
+
# :values. If there are values that match, they replace the
|
137
|
+
# original option value. If none, then the original option
|
138
|
+
# value is used.
|
165
139
|
def initialize(opts)
|
166
140
|
@defaults = {}
|
167
141
|
@opt_aliases = {}
|
@@ -187,10 +161,14 @@ module Boson
|
|
187
161
|
@option_attributes[nice_name] = type
|
188
162
|
@opt_aliases[nice_name] = Array(type[:alias]) if type.key?(:alias)
|
189
163
|
@defaults[nice_name] = type[:default] if type[:default]
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
164
|
+
if (type.key?(:values) || type.key?(:keys)) && !type.key?(:enum)
|
165
|
+
@option_attributes[nice_name][:enum] = true
|
166
|
+
end
|
167
|
+
if type.key?(:keys)
|
168
|
+
@option_attributes[nice_name][:default_keys] ||= type[:keys][0]
|
169
|
+
end
|
170
|
+
type = type[:type] || (!type[:default].nil? ?
|
171
|
+
determine_option_type(type[:default]) : :boolean)
|
194
172
|
end
|
195
173
|
|
196
174
|
# set defaults
|
@@ -207,10 +185,13 @@ module Boson
|
|
207
185
|
@opt_aliases = @opt_aliases.sort.inject({}) {|h, (nice_name, aliases)|
|
208
186
|
name = dasherize nice_name
|
209
187
|
# allow for aliases as symbols
|
210
|
-
aliases.map! {|e|
|
188
|
+
aliases.map! {|e|
|
189
|
+
e.to_s.index('-') == 0 || e == false ? e : dasherize(e.to_s) }
|
190
|
+
|
211
191
|
if aliases.empty? and nice_name.length > 1
|
212
192
|
opt_alias = nice_name[0,1]
|
213
|
-
opt_alias = h.key?("-"+opt_alias) ? "-"+opt_alias.capitalize :
|
193
|
+
opt_alias = h.key?("-"+opt_alias) ? "-"+opt_alias.capitalize :
|
194
|
+
"-"+opt_alias
|
214
195
|
h[opt_alias] ||= name unless @opt_types.key?(opt_alias)
|
215
196
|
else
|
216
197
|
aliases.each {|e| h[e] = name if !@opt_types.key?(e) && e != false }
|
@@ -219,20 +200,24 @@ module Boson
|
|
219
200
|
}
|
220
201
|
end
|
221
202
|
|
222
|
-
# Parses an array of arguments for defined options to return an
|
223
|
-
# recognizes a valid option, it continues to
|
224
|
-
#
|
225
|
-
#
|
226
|
-
#
|
227
|
-
#
|
203
|
+
# Parses an array of arguments for defined options to return an indifferent
|
204
|
+
# access hash. Once the parser recognizes a valid option, it continues to
|
205
|
+
# parse until an non option argument is detected.
|
206
|
+
# @param [Hash] flags
|
207
|
+
# @option flags [Boolean] :opts_before_args When true options must come
|
208
|
+
# before arguments. Default is false.
|
209
|
+
# @option flags [Boolean] :delete_invalid_opts When true deletes any
|
210
|
+
# invalid options left after parsing. Will stop deleting if it comes
|
211
|
+
# across - or --. Default is false.
|
228
212
|
def parse(args, flags={})
|
229
213
|
@args = args
|
230
|
-
# start with defaults
|
231
|
-
hash =
|
232
|
-
|
214
|
+
# start with symbolized defaults
|
215
|
+
hash = Hash[@defaults.map {|k,v| [k.to_sym, v] }]
|
216
|
+
|
233
217
|
@leading_non_opts = []
|
234
218
|
unless flags[:opts_before_args]
|
235
|
-
@leading_non_opts << shift until current_is_option? || @args.empty? ||
|
219
|
+
@leading_non_opts << shift until current_is_option? || @args.empty? ||
|
220
|
+
STOP_STRINGS.include?(peek)
|
236
221
|
end
|
237
222
|
|
238
223
|
while current_is_option?
|
@@ -259,11 +244,11 @@ module Boson
|
|
259
244
|
@trailing_non_opts = @args
|
260
245
|
check_required! hash
|
261
246
|
delete_invalid_opts if flags[:delete_invalid_opts]
|
262
|
-
hash
|
247
|
+
indifferent_hash.tap {|h| h.update hash }
|
263
248
|
end
|
264
249
|
|
265
|
-
# Helper method to generate usage. Takes a dashed option and a string value
|
266
|
-
# an option value's format.
|
250
|
+
# Helper method to generate usage. Takes a dashed option and a string value
|
251
|
+
# indicating an option value's format.
|
267
252
|
def default_usage(opt, val)
|
268
253
|
opt + "=" + (@defaults[undasherize(opt)] || val).to_s
|
269
254
|
end
|
@@ -272,7 +257,8 @@ module Boson
|
|
272
257
|
def formatted_usage
|
273
258
|
return "" if @opt_types.empty?
|
274
259
|
@opt_types.map do |opt, type|
|
275
|
-
val = respond_to?("usage_for_#{type}", true) ?
|
260
|
+
val = respond_to?("usage_for_#{type}", true) ?
|
261
|
+
send("usage_for_#{type}", opt) : "#{opt}=:#{type}"
|
276
262
|
"[" + val + "]"
|
277
263
|
end.join(" ")
|
278
264
|
end
|
@@ -280,47 +266,37 @@ module Boson
|
|
280
266
|
alias :to_s :formatted_usage
|
281
267
|
|
282
268
|
# More verbose option help in the form of a table.
|
283
|
-
def print_usage_table(
|
284
|
-
|
285
|
-
fields =
|
286
|
-
(fields
|
287
|
-
opts = all_options_with_fields fields
|
288
|
-
fields.delete(:default) if fields.include?(:default) && opts.all? {|e| e[:default].nil? }
|
289
|
-
render_options = default_render_options.merge(:fields=>fields).merge(render_options)
|
290
|
-
View.render opts, render_options
|
269
|
+
def print_usage_table(options={})
|
270
|
+
fields = get_usage_fields options[:fields]
|
271
|
+
fields, opts = get_fields_and_options(fields, options)
|
272
|
+
render_table(fields, opts, options)
|
291
273
|
end
|
292
274
|
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
h[:default] = @defaults[nice_name] if fields.include?(:default)
|
299
|
-
(fields - h.keys).each {|f|
|
300
|
-
h[f] = (option_attributes[nice_name] || {})[f]
|
301
|
-
}
|
302
|
-
t << h
|
303
|
-
}
|
304
|
-
end
|
275
|
+
module API
|
276
|
+
def get_fields_and_options(fields, options)
|
277
|
+
opts = all_options_with_fields fields
|
278
|
+
[fields, opts]
|
279
|
+
end
|
305
280
|
|
306
|
-
|
307
|
-
|
308
|
-
|
281
|
+
def render_table(fields, arr, options)
|
282
|
+
headers = options[:no_headers] ? [] : [['Name', 'Desc'], ['----', '----']]
|
283
|
+
arr_of_arr = headers + arr.map do |row|
|
284
|
+
[ row.values_at(:alias, :name).compact.join(', '), row[:desc].to_s ]
|
285
|
+
end
|
286
|
+
puts Util.format_table(arr_of_arr)
|
287
|
+
end
|
309
288
|
end
|
289
|
+
include API
|
310
290
|
|
311
291
|
# Hash of option names mapped to hash of its external attributes
|
312
292
|
def option_attributes
|
313
293
|
@option_attributes || {}
|
314
294
|
end
|
315
295
|
|
316
|
-
def get_usage_fields(fields) #:nodoc:
|
317
|
-
fields || ([:name, :alias, :type] + [:desc, :values, :keys].select {|e|
|
318
|
-
option_attributes.values.any? {|f| f.key?(e) } }).uniq
|
319
|
-
end
|
320
|
-
|
321
296
|
# Hash of option attributes for the currently parsed option. _Any_ hash keys
|
322
|
-
# passed to an option are available here. This means that an option type can
|
323
|
-
# user-defined attributes available during option parsing and
|
297
|
+
# passed to an option are available here. This means that an option type can
|
298
|
+
# have any user-defined attributes available during option parsing and
|
299
|
+
# object creation.
|
324
300
|
def current_attributes
|
325
301
|
@option_attributes && @option_attributes[@current_option] || {}
|
326
302
|
end
|
@@ -350,15 +326,39 @@ module Boson
|
|
350
326
|
@opt_aliases.keys.map {|e| undasherize e }
|
351
327
|
end
|
352
328
|
|
329
|
+
# Creates a Hash with indifferent access
|
330
|
+
def indifferent_hash
|
331
|
+
Hash.new {|hash,key| hash[key.to_sym] if String === key }
|
332
|
+
end
|
333
|
+
|
334
|
+
private
|
335
|
+
def all_options_with_fields(fields)
|
336
|
+
aliases = @opt_aliases.invert
|
337
|
+
@opt_types.keys.sort.inject([]) {|t,e|
|
338
|
+
nice_name = undasherize(e)
|
339
|
+
h = {:name=>e, :type=>@opt_types[e], :alias=>aliases[e] || nil }
|
340
|
+
h[:default] = @defaults[nice_name] if fields.include?(:default)
|
341
|
+
(fields - h.keys).each {|f|
|
342
|
+
h[f] = (option_attributes[nice_name] || {})[f]
|
343
|
+
}
|
344
|
+
t << h
|
345
|
+
}
|
346
|
+
end
|
347
|
+
|
348
|
+
def get_usage_fields(fields)
|
349
|
+
fields || ([:name, :alias, :type] + [:desc, :values, :keys].select {|e|
|
350
|
+
option_attributes.values.any? {|f| f.key?(e) } }).uniq
|
351
|
+
end
|
352
|
+
|
353
353
|
def option_type(opt)
|
354
354
|
if opt =~ /^--no-(\w+)$/
|
355
|
-
@opt_types[opt] || @opt_types[dasherize($1)] ||
|
355
|
+
@opt_types[opt] || @opt_types[dasherize($1)] ||
|
356
|
+
@opt_types[original_no_opt($1)]
|
356
357
|
else
|
357
358
|
@opt_types[opt]
|
358
359
|
end
|
359
360
|
end
|
360
361
|
|
361
|
-
private
|
362
362
|
def determine_option_type(value)
|
363
363
|
return value if value.is_a?(Symbol)
|
364
364
|
case value
|
@@ -375,23 +375,26 @@ module Boson
|
|
375
375
|
end
|
376
376
|
|
377
377
|
def create_option_value(type)
|
378
|
-
if current_attributes.key?(:bool_default) &&
|
378
|
+
if current_attributes.key?(:bool_default) &&
|
379
|
+
(@original_current_option !~ EQ_RE) &&
|
379
380
|
!(bool_default = current_attributes[:bool_default]).is_a?(String)
|
380
381
|
bool_default
|
381
382
|
else
|
382
|
-
respond_to?("create_#{type}", true) ?
|
383
|
-
|
383
|
+
respond_to?("create_#{type}", true) ?
|
384
|
+
send("create_#{type}", type != :boolean ? value_shift : nil) :
|
385
|
+
raise(Error, "Option '#{@current_option}' is invalid option type " +
|
386
|
+
"#{type.inspect}.")
|
384
387
|
end
|
385
388
|
end
|
386
389
|
|
387
390
|
def auto_alias_value(values, possible_value)
|
388
|
-
if Boson.
|
389
|
-
self.class.send(:define_method, :auto_alias_value) {|values,
|
390
|
-
Util.underscore_search(
|
391
|
+
if Boson.config[:option_underscore_search]
|
392
|
+
self.class.send(:define_method, :auto_alias_value) {|values, possible_val|
|
393
|
+
Util.underscore_search(possible_val, values, true) || possible_val
|
391
394
|
}.call(values, possible_value)
|
392
395
|
else
|
393
|
-
self.class.send(:define_method, :auto_alias_value) {|values,
|
394
|
-
values.find {|v| v.to_s =~ /^#{
|
396
|
+
self.class.send(:define_method, :auto_alias_value) {|values, possible_val|
|
397
|
+
values.find {|v| v.to_s =~ /^#{possible_val}/ } || possible_val
|
395
398
|
}.call(values, possible_value)
|
396
399
|
end
|
397
400
|
end
|
@@ -399,7 +402,9 @@ module Boson
|
|
399
402
|
def validate_enum_values(values, possible_values)
|
400
403
|
if current_attributes[:enum]
|
401
404
|
Array(possible_values).each {|e|
|
402
|
-
|
405
|
+
if !values.include?(e)
|
406
|
+
raise(Error, "invalid value '#{e}' for option '#{@current_option}'")
|
407
|
+
end
|
403
408
|
}
|
404
409
|
end
|
405
410
|
end
|
@@ -416,7 +421,7 @@ module Boson
|
|
416
421
|
@trailing_non_opts.delete_if {|e|
|
417
422
|
break if STOP_STRINGS.include? e
|
418
423
|
invalid = e.to_s[/^-/]
|
419
|
-
|
424
|
+
warn "Deleted invalid option '#{e}'" if invalid
|
420
425
|
invalid
|
421
426
|
}
|
422
427
|
end
|
@@ -436,7 +441,7 @@ module Boson
|
|
436
441
|
@args = arg + @args
|
437
442
|
end
|
438
443
|
end
|
439
|
-
|
444
|
+
|
440
445
|
def valid?(arg)
|
441
446
|
if arg.to_s =~ /^--no-(\w+)$/
|
442
447
|
@opt_types.key?(arg) or (@opt_types[dasherize($1)] == :boolean) or
|
@@ -454,11 +459,11 @@ module Boson
|
|
454
459
|
$1.split('').any? { |f| valid?("-#{f}") }
|
455
460
|
end
|
456
461
|
end
|
457
|
-
|
462
|
+
|
458
463
|
def normalize_option(opt)
|
459
464
|
@opt_aliases.key?(opt) ? @opt_aliases[opt] : opt
|
460
465
|
end
|
461
|
-
|
466
|
+
|
462
467
|
def original_no_opt(opt)
|
463
468
|
@opt_aliases[dasherize(opt)]
|
464
469
|
end
|