boson 0.4.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|