botanicus-thor 0.9.8
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/CHANGELOG.rdoc +52 -0
- data/LICENSE +20 -0
- data/README.markdown +76 -0
- data/Rakefile +6 -0
- data/bin/rake2thor +88 -0
- data/bin/thor +7 -0
- data/lib/thor/error.rb +5 -0
- data/lib/thor/options.rb +269 -0
- data/lib/thor/runner.rb +344 -0
- data/lib/thor/task.rb +85 -0
- data/lib/thor/task_hash.rb +23 -0
- data/lib/thor/tasks/package.rb +20 -0
- data/lib/thor/tasks.rb +88 -0
- data/lib/thor/util.rb +77 -0
- data/lib/thor.rb +177 -0
- metadata +72 -0
data/CHANGELOG.rdoc
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
== TODO
|
2
|
+
|
3
|
+
* Change Thor.start to parse ARGV in a single pass
|
4
|
+
* Improve spec coverage for Thor::Runner
|
5
|
+
* Improve help output to list shorthand switches, too
|
6
|
+
* Investigate and fix deep namespacing ("foo:bar:baz") issues
|
7
|
+
|
8
|
+
== 0.9.8, released 2008-10-20
|
9
|
+
|
10
|
+
* Fixed some tiny issues that were introduced lately.
|
11
|
+
|
12
|
+
== 0.9.7, released 2008-10-13
|
13
|
+
|
14
|
+
* Setting global method options on the initialize method works as expected:
|
15
|
+
All other tasks will accept these global options in addition to their own.
|
16
|
+
* Added 'group' notion to Thor task sets (class Thor); by default all tasks
|
17
|
+
are in the 'standard' group. Running 'thor -T' will only show the standard
|
18
|
+
tasks - adding --all will show all tasks. You can also filter on a specific
|
19
|
+
group using the --group option: thor -T --group advanced
|
20
|
+
|
21
|
+
== 0.9.6, released 2008-09-13
|
22
|
+
|
23
|
+
* Generic improvements
|
24
|
+
|
25
|
+
== 0.9.5, released 2008-08-27
|
26
|
+
|
27
|
+
* Improve Windows compatibility
|
28
|
+
* Update (incorrect) README and task.thor sample file
|
29
|
+
* Options hash is now frozen (once returned)
|
30
|
+
* Allow magic predicates on options object. For instance: `options.force?`
|
31
|
+
* Add support for :numeric type
|
32
|
+
* BACKWARDS INCOMPATIBLE: Refactor Thor::Options. You cannot access shorthand forms in options hash anymore (for instance, options[:f])
|
33
|
+
* Allow specifying optional args with default values: method_options(:user => "mislav")
|
34
|
+
* Don't write options for nil or false values. This allows, for example, turning color off when running specs.
|
35
|
+
* Exit with the status of the spec command to help CI stuff out some.
|
36
|
+
|
37
|
+
== 0.9.4, released 2008-08-13
|
38
|
+
|
39
|
+
* Try to add Windows compatibility.
|
40
|
+
* BACKWARDS INCOMPATIBLE: options hash is now accessed as a property in your class and is not passed as last argument anymore
|
41
|
+
* Allow options at the beginning of the argument list as well as the end.
|
42
|
+
* Make options available with symbol keys in addition to string keys.
|
43
|
+
* Allow true to be passed to Thor#method_options to denote a boolean option.
|
44
|
+
* If loading a thor file fails, don't give up, just print a warning and keep going.
|
45
|
+
* Make sure that we re-raise errors if they happened further down the pipe than we care about.
|
46
|
+
* Only delete the old file on updating when the installation of the new one is a success
|
47
|
+
* Make it Ruby 1.8.5 compatible.
|
48
|
+
* Don't raise an error if a boolean switch is defined multiple times.
|
49
|
+
* Thor::Options now doesn't parse through things that look like options but aren't.
|
50
|
+
* Add URI detection to install task, and make sure we don't append ".thor" to URIs
|
51
|
+
* Add rake2thor to the gem binfiles.
|
52
|
+
* Make sure local Thorfiles override system-wide ones.
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Yehuda Katz
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
thor
|
2
|
+
====
|
3
|
+
|
4
|
+
Map options to a class. Simply create a class with the appropriate annotations, and have options automatically map
|
5
|
+
to functions and parameters.
|
6
|
+
|
7
|
+
Example:
|
8
|
+
|
9
|
+
class MyApp < Thor # [1]
|
10
|
+
map "-L" => :list # [2]
|
11
|
+
|
12
|
+
desc "install APP_NAME", "install one of the available apps" # [3]
|
13
|
+
method_options :force => :boolean, :alias => :optional # [4]
|
14
|
+
def install(name)
|
15
|
+
user_alias = options[:alias]
|
16
|
+
if options.force?
|
17
|
+
# do something
|
18
|
+
end
|
19
|
+
# ... other code ...
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "list [SEARCH]", "list all of the available apps, limited by SEARCH"
|
23
|
+
def list(search = "")
|
24
|
+
# list everything
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
Thor automatically maps commands as such:
|
29
|
+
|
30
|
+
app install myname --force
|
31
|
+
|
32
|
+
That gets converted to:
|
33
|
+
|
34
|
+
MyApp.new.install("myname")
|
35
|
+
# with {'force' => true} as options hash
|
36
|
+
|
37
|
+
1. Inherit from Thor to turn a class into an option mapper
|
38
|
+
2. Map additional non-valid identifiers to specific methods. In this case,
|
39
|
+
convert -L to :list
|
40
|
+
3. Describe the method immediately below. The first parameter is the usage information,
|
41
|
+
and the second parameter is the description.
|
42
|
+
4. Provide any additional options. These will be marshaled from `--` and `-` params.
|
43
|
+
In this case, a `--force` and a `-f` option is added.
|
44
|
+
|
45
|
+
Types for `method_options`
|
46
|
+
--------------------------
|
47
|
+
|
48
|
+
<dl>
|
49
|
+
<dt><code>:boolean</code></dt>
|
50
|
+
<dd>true if the option is passed</dd>
|
51
|
+
<dt><code>true or false</code></dt>
|
52
|
+
<dd>same as <code>:boolean</code>, but fall back to given boolean as default value</dd>
|
53
|
+
<dt><code>:required</code></dt>
|
54
|
+
<dd>the value for this option MUST be provided</dd>
|
55
|
+
<dt><code>:optional</code></dt>
|
56
|
+
<dd>the value for this option MAY be provided</dd>
|
57
|
+
<dt><code>:numeric</code></dt>
|
58
|
+
<dd>the value MAY be provided, but MUST be in numeric form</dd>
|
59
|
+
<dt>a String or Numeric</dt>
|
60
|
+
<dd>same as <code>:optional</code>, but fall back to the given object as default value</dd>
|
61
|
+
</dl>
|
62
|
+
|
63
|
+
In case of unsatisfied requirements, `Thor::Options::Error` is raised.
|
64
|
+
|
65
|
+
Examples of option parsing:
|
66
|
+
|
67
|
+
# let's say this is how we defined options for a method:
|
68
|
+
method_options(:force => :boolean, :retries => :numeric)
|
69
|
+
|
70
|
+
# here is how the following command-line invocations would be parsed:
|
71
|
+
|
72
|
+
command -f --retries 5 # => {'force' => true, 'retries' => 5}
|
73
|
+
command --force -r=5 # => {'force' => true, 'retries' => 5}
|
74
|
+
command -fr 5 # => {'force' => true, 'retries' => 5}
|
75
|
+
command --retries=5 # => {'retries' => 5}
|
76
|
+
command -r5 # => {'retries' => 5}
|
data/Rakefile
ADDED
data/bin/rake2thor
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'ruby2ruby'
|
6
|
+
require 'parse_tree'
|
7
|
+
if Ruby2Ruby::VERSION >= "1.2.0"
|
8
|
+
require 'parse_tree_extensions'
|
9
|
+
end
|
10
|
+
require 'rake'
|
11
|
+
|
12
|
+
input = ARGV[0] || 'Rakefile'
|
13
|
+
output = ARGV[1] || 'Thorfile'
|
14
|
+
|
15
|
+
$requires = []
|
16
|
+
|
17
|
+
module Kernel
|
18
|
+
def require_with_record(file)
|
19
|
+
$requires << file if caller[1] =~ /rake2thor:/
|
20
|
+
require_without_record file
|
21
|
+
end
|
22
|
+
alias_method :require_without_record, :require
|
23
|
+
alias_method :require, :require_with_record
|
24
|
+
end
|
25
|
+
|
26
|
+
load input
|
27
|
+
|
28
|
+
@private_methods = []
|
29
|
+
|
30
|
+
def file_task_name(name)
|
31
|
+
"compile_" + name.gsub('/', '_slash_').gsub('.', '_dot_').gsub(/\W/, '_')
|
32
|
+
end
|
33
|
+
|
34
|
+
def method_for_task(task)
|
35
|
+
file_task = task.is_a?(Rake::FileTask)
|
36
|
+
comment = task.instance_variable_get('@comment')
|
37
|
+
prereqs = task.instance_variable_get('@prerequisites').select(&Rake::Task.method(:task_defined?))
|
38
|
+
actions = task.instance_variable_get('@actions')
|
39
|
+
name = task.name.gsub(/^([^:]+:)+/, '')
|
40
|
+
name = file_task_name(name) if file_task
|
41
|
+
meth = ''
|
42
|
+
|
43
|
+
meth << "desc #{name.inspect}, #{comment.inspect}\n" if comment
|
44
|
+
meth << "def #{name}\n"
|
45
|
+
|
46
|
+
meth << prereqs.map do |pre|
|
47
|
+
pre = pre.to_s
|
48
|
+
pre = file_task_name(pre) if Rake::Task[pre].is_a?(Rake::FileTask)
|
49
|
+
' ' + pre
|
50
|
+
end.join("\n")
|
51
|
+
|
52
|
+
meth << "\n\n" unless prereqs.empty? || actions.empty?
|
53
|
+
|
54
|
+
meth << actions.map do |act|
|
55
|
+
act = act.to_ruby
|
56
|
+
unless act.gsub!(/^proc \{ \|(\w+)\|\n/,
|
57
|
+
" \\1 = Struct.new(:name).new(#{name.inspect}) # A crude mock Rake::Task object\n")
|
58
|
+
act.gsub!(/^proc \{\n/, '')
|
59
|
+
end
|
60
|
+
act.gsub(/\n\}$/, '')
|
61
|
+
end.join("\n")
|
62
|
+
|
63
|
+
meth << "\nend"
|
64
|
+
|
65
|
+
if file_task
|
66
|
+
@private_methods << meth
|
67
|
+
return
|
68
|
+
end
|
69
|
+
|
70
|
+
meth
|
71
|
+
end
|
72
|
+
|
73
|
+
body = Rake::Task.tasks.map(&method(:method_for_task)).compact.map { |meth| meth.gsub(/^/, ' ') }.join("\n\n")
|
74
|
+
|
75
|
+
unless @private_methods.empty?
|
76
|
+
body << "\n\n private\n\n"
|
77
|
+
body << @private_methods.map { |meth| meth.gsub(/^/, ' ') }.join("\n\n")
|
78
|
+
end
|
79
|
+
|
80
|
+
requires = $requires.map { |r| "require #{r.inspect}" }.join("\n")
|
81
|
+
|
82
|
+
File.open(output, 'w') { |f| f.write(<<END.lstrip) }
|
83
|
+
#{requires}
|
84
|
+
|
85
|
+
class Default < Thor
|
86
|
+
#{body}
|
87
|
+
end
|
88
|
+
END
|
data/bin/thor
ADDED
data/lib/thor/error.rb
ADDED
data/lib/thor/options.rb
ADDED
@@ -0,0 +1,269 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
# This is a modified version of Daniel Berger's Getopt::Long class,
|
4
|
+
# licensed under Ruby's license.
|
5
|
+
|
6
|
+
class Thor
|
7
|
+
class Options
|
8
|
+
class Error < StandardError; end
|
9
|
+
|
10
|
+
# simple Hash with indifferent access
|
11
|
+
class Hash < ::Hash
|
12
|
+
def initialize(hash)
|
13
|
+
super()
|
14
|
+
update hash
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](key)
|
18
|
+
super convert_key(key)
|
19
|
+
end
|
20
|
+
|
21
|
+
def values_at(*indices)
|
22
|
+
indices.collect { |key| self[convert_key(key)] }
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
def convert_key(key)
|
27
|
+
key.kind_of?(Symbol) ? key.to_s : key
|
28
|
+
end
|
29
|
+
|
30
|
+
# Magic predicates. For instance:
|
31
|
+
# options.force? # => !!options['force']
|
32
|
+
def method_missing(method, *args, &block)
|
33
|
+
method = method.to_s
|
34
|
+
if method =~ /^(\w+)=$/
|
35
|
+
self[$1] = args.first
|
36
|
+
elsif method =~ /^(\w+)\?$/
|
37
|
+
!!self[$1]
|
38
|
+
else
|
39
|
+
self[method]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
NUMERIC = /(\d*\.\d+|\d+)/
|
45
|
+
LONG_RE = /^(--\w+[-\w+]*)$/
|
46
|
+
SHORT_RE = /^(-[a-z])$/i
|
47
|
+
EQ_RE = /^(--\w+[-\w+]*|-[a-z])=(.*)$/i
|
48
|
+
SHORT_SQ_RE = /^-([a-z]{2,})$/i # Allow either -x -v or -xv style for single char args
|
49
|
+
SHORT_NUM = /^(-[a-z])#{NUMERIC}$/i
|
50
|
+
|
51
|
+
attr_reader :leading_non_opts, :trailing_non_opts
|
52
|
+
|
53
|
+
def non_opts
|
54
|
+
leading_non_opts + trailing_non_opts
|
55
|
+
end
|
56
|
+
|
57
|
+
# Takes an array of switches. Each array consists of up to three
|
58
|
+
# elements that indicate the name and type of switch. Returns a hash
|
59
|
+
# containing each switch name, minus the '-', as a key. The value
|
60
|
+
# for each key depends on the type of switch and/or the value provided
|
61
|
+
# by the user.
|
62
|
+
#
|
63
|
+
# The long switch _must_ be provided. The short switch defaults to the
|
64
|
+
# first letter of the short switch. The default type is :boolean.
|
65
|
+
#
|
66
|
+
# Example:
|
67
|
+
#
|
68
|
+
# opts = Thor::Options.new(
|
69
|
+
# "--debug" => true,
|
70
|
+
# ["--verbose", "-v"] => true,
|
71
|
+
# ["--level", "-l"] => :numeric
|
72
|
+
# ).parse(args)
|
73
|
+
#
|
74
|
+
def initialize(switches)
|
75
|
+
@defaults = {}
|
76
|
+
@shorts = {}
|
77
|
+
|
78
|
+
@leading_non_opts, @trailing_non_opts = [], []
|
79
|
+
|
80
|
+
@switches = switches.inject({}) do |mem, (name, type)|
|
81
|
+
if name.is_a?(Array)
|
82
|
+
name, *shorts = name
|
83
|
+
else
|
84
|
+
name = name.to_s
|
85
|
+
shorts = []
|
86
|
+
end
|
87
|
+
# we need both nice and dasherized form of switch name
|
88
|
+
if name.index('-') == 0
|
89
|
+
nice_name = undasherize name
|
90
|
+
else
|
91
|
+
nice_name = name
|
92
|
+
name = dasherize name
|
93
|
+
end
|
94
|
+
# if there are no shortcuts specified, generate one using the first character
|
95
|
+
shorts << "-" + nice_name[0,1] if shorts.empty? and nice_name.length > 1
|
96
|
+
shorts.each { |short| @shorts[short] = name }
|
97
|
+
|
98
|
+
# normalize type
|
99
|
+
case type
|
100
|
+
when TrueClass
|
101
|
+
@defaults[nice_name] = true
|
102
|
+
type = :boolean
|
103
|
+
when FalseClass
|
104
|
+
@defaults[nice_name] = false
|
105
|
+
type = :boolean
|
106
|
+
when String
|
107
|
+
@defaults[nice_name] = type
|
108
|
+
type = :optional
|
109
|
+
when Numeric
|
110
|
+
@defaults[nice_name] = type
|
111
|
+
type = :numeric
|
112
|
+
end
|
113
|
+
|
114
|
+
mem[name] = type
|
115
|
+
mem
|
116
|
+
end
|
117
|
+
|
118
|
+
# remove shortcuts that happen to coincide with any of the main switches
|
119
|
+
@shorts.keys.each do |short|
|
120
|
+
@shorts.delete(short) if @switches.key?(short)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def parse(args, skip_leading_non_opts = true)
|
125
|
+
@args = args
|
126
|
+
# start with Thor::Options::Hash pre-filled with defaults
|
127
|
+
hash = Hash.new @defaults
|
128
|
+
|
129
|
+
@leading_non_opts = []
|
130
|
+
if skip_leading_non_opts
|
131
|
+
@leading_non_opts << shift until current_is_option? || @args.empty?
|
132
|
+
end
|
133
|
+
|
134
|
+
while current_is_option?
|
135
|
+
case shift
|
136
|
+
when SHORT_SQ_RE
|
137
|
+
unshift $1.split('').map { |f| "-#{f}" }
|
138
|
+
next
|
139
|
+
when EQ_RE, SHORT_NUM
|
140
|
+
unshift $2
|
141
|
+
switch = $1
|
142
|
+
when LONG_RE, SHORT_RE
|
143
|
+
switch = $1
|
144
|
+
end
|
145
|
+
|
146
|
+
switch = normalize_switch(switch)
|
147
|
+
nice_name = undasherize(switch)
|
148
|
+
type = switch_type(switch)
|
149
|
+
|
150
|
+
case type
|
151
|
+
when :required
|
152
|
+
assert_value!(switch)
|
153
|
+
raise Error, "cannot pass switch '#{peek}' as an argument" if valid?(peek)
|
154
|
+
hash[nice_name] = shift
|
155
|
+
when :optional
|
156
|
+
hash[nice_name] = peek.nil? || valid?(peek) || shift
|
157
|
+
when :boolean
|
158
|
+
if !@switches.key?(switch) && nice_name =~ /^no-(\w+)$/
|
159
|
+
hash[$1] = false
|
160
|
+
else
|
161
|
+
hash[nice_name] = true
|
162
|
+
end
|
163
|
+
|
164
|
+
when :numeric
|
165
|
+
assert_value!(switch)
|
166
|
+
unless peek =~ NUMERIC and $& == peek
|
167
|
+
raise Error, "expected numeric value for '#{switch}'; got #{peek.inspect}"
|
168
|
+
end
|
169
|
+
hash[nice_name] = $&.index('.') ? shift.to_f : shift.to_i
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
@trailing_non_opts = @args
|
174
|
+
|
175
|
+
check_required! hash
|
176
|
+
hash.freeze
|
177
|
+
hash
|
178
|
+
end
|
179
|
+
|
180
|
+
def formatted_usage
|
181
|
+
return "" if @switches.empty?
|
182
|
+
@switches.map do |opt, type|
|
183
|
+
case type
|
184
|
+
when :boolean
|
185
|
+
"[#{opt}]"
|
186
|
+
when :required
|
187
|
+
opt + "=" + opt.gsub(/\-/, "").upcase
|
188
|
+
else
|
189
|
+
sample = @defaults[undasherize(opt)]
|
190
|
+
sample ||= case type
|
191
|
+
when :optional then undasherize(opt).gsub(/\-/, "_").upcase
|
192
|
+
when :numeric then "N"
|
193
|
+
end
|
194
|
+
"[" + opt + "=" + sample.to_s + "]"
|
195
|
+
end
|
196
|
+
end.join(" ")
|
197
|
+
end
|
198
|
+
|
199
|
+
alias :to_s :formatted_usage
|
200
|
+
|
201
|
+
private
|
202
|
+
|
203
|
+
def assert_value!(switch)
|
204
|
+
raise Error, "no value provided for argument '#{switch}'" if peek.nil?
|
205
|
+
end
|
206
|
+
|
207
|
+
def undasherize(str)
|
208
|
+
str.sub(/^-{1,2}/, '')
|
209
|
+
end
|
210
|
+
|
211
|
+
def dasherize(str)
|
212
|
+
(str.length > 1 ? "--" : "-") + str
|
213
|
+
end
|
214
|
+
|
215
|
+
def peek
|
216
|
+
@args.first
|
217
|
+
end
|
218
|
+
|
219
|
+
def shift
|
220
|
+
@args.shift
|
221
|
+
end
|
222
|
+
|
223
|
+
def unshift(arg)
|
224
|
+
unless arg.kind_of?(Array)
|
225
|
+
@args.unshift(arg)
|
226
|
+
else
|
227
|
+
@args = arg + @args
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def valid?(arg)
|
232
|
+
if arg.to_s =~ /^--no-(\w+)$/
|
233
|
+
@switches.key?(arg) or (@switches["--#{$1}"] == :boolean)
|
234
|
+
else
|
235
|
+
@switches.key?(arg) or @shorts.key?(arg)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def current_is_option?
|
240
|
+
case peek
|
241
|
+
when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM
|
242
|
+
valid?($1)
|
243
|
+
when SHORT_SQ_RE
|
244
|
+
$1.split('').any? { |f| valid?("-#{f}") }
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def normalize_switch(switch)
|
249
|
+
@shorts.key?(switch) ? @shorts[switch] : switch
|
250
|
+
end
|
251
|
+
|
252
|
+
def switch_type(switch)
|
253
|
+
if switch =~ /^--no-(\w+)$/
|
254
|
+
@switches[switch] || @switches["--#{$1}"]
|
255
|
+
else
|
256
|
+
@switches[switch]
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def check_required!(hash)
|
261
|
+
for name, type in @switches
|
262
|
+
if type == :required and !hash[undasherize(name)]
|
263
|
+
raise Error, "no value provided for required argument '#{name}'"
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
end
|
269
|
+
end
|