clive 0.8.1 → 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/LICENSE +1 -1
- data/README.md +328 -227
- data/lib/clive.rb +130 -50
- data/lib/clive/argument.rb +170 -0
- data/lib/clive/arguments.rb +139 -0
- data/lib/clive/arguments/parser.rb +210 -0
- data/lib/clive/base.rb +189 -0
- data/lib/clive/command.rb +342 -444
- data/lib/clive/error.rb +66 -0
- data/lib/clive/formatter.rb +57 -141
- data/lib/clive/formatter/colour.rb +37 -0
- data/lib/clive/formatter/plain.rb +172 -0
- data/lib/clive/option.rb +185 -75
- data/lib/clive/option/runner.rb +163 -0
- data/lib/clive/output.rb +141 -16
- data/lib/clive/parser.rb +180 -87
- data/lib/clive/struct_hash.rb +109 -0
- data/lib/clive/type.rb +117 -0
- data/lib/clive/type/definitions.rb +170 -0
- data/lib/clive/type/lookup.rb +23 -0
- data/lib/clive/version.rb +3 -3
- data/spec/clive/a_cli_spec.rb +245 -0
- data/spec/clive/argument_spec.rb +148 -0
- data/spec/clive/arguments/parser_spec.rb +35 -0
- data/spec/clive/arguments_spec.rb +191 -0
- data/spec/clive/command_spec.rb +276 -209
- data/spec/clive/formatter/colour_spec.rb +129 -0
- data/spec/clive/formatter/plain_spec.rb +129 -0
- data/spec/clive/option/runner_spec.rb +92 -0
- data/spec/clive/option_spec.rb +149 -23
- data/spec/clive/output_spec.rb +86 -2
- data/spec/clive/parser_spec.rb +201 -81
- data/spec/clive/struct_hash_spec.rb +82 -0
- data/spec/clive/type/definitions_spec.rb +312 -0
- data/spec/clive/type_spec.rb +107 -0
- data/spec/clive_spec.rb +60 -0
- data/spec/extras/expectations.rb +86 -0
- data/spec/extras/focus.rb +22 -0
- data/spec/helper.rb +35 -0
- metadata +56 -36
- data/lib/clive/bool.rb +0 -67
- data/lib/clive/exceptions.rb +0 -54
- data/lib/clive/flag.rb +0 -199
- data/lib/clive/switch.rb +0 -31
- data/lib/clive/tokens.rb +0 -141
- data/spec/clive/bool_spec.rb +0 -54
- data/spec/clive/flag_spec.rb +0 -117
- data/spec/clive/formatter_spec.rb +0 -108
- data/spec/clive/switch_spec.rb +0 -14
- data/spec/clive/tokens_spec.rb +0 -38
- data/spec/shared_specs.rb +0 -16
- data/spec/spec_helper.rb +0 -12
@@ -0,0 +1,210 @@
|
|
1
|
+
class Clive
|
2
|
+
class Arguments
|
3
|
+
class Parser
|
4
|
+
|
5
|
+
# Raised when the argument string passed to {Option} is wrong.
|
6
|
+
class InvalidArgumentStringError < Error
|
7
|
+
reason 'Invalid argument string format: #1'
|
8
|
+
end
|
9
|
+
|
10
|
+
# Valid key names for creating arguments passed to Option#initialize and
|
11
|
+
# standard names to map them to.
|
12
|
+
KEYS = {
|
13
|
+
:arg => [:args],
|
14
|
+
:type => [:types, :kind, :as],
|
15
|
+
:match => [:matches],
|
16
|
+
:within => [:withins, :in],
|
17
|
+
:default => [:defaults],
|
18
|
+
:constraint => [:constraints]
|
19
|
+
}.inject({}) {|hsh, (k,v)|
|
20
|
+
(v + [k]).each {|key|
|
21
|
+
hsh[key] = k
|
22
|
+
}
|
23
|
+
hsh
|
24
|
+
}
|
25
|
+
|
26
|
+
# @param opts [Hash]
|
27
|
+
def initialize(opts)
|
28
|
+
@opts = normalise_key_names(opts, KEYS) || {}
|
29
|
+
end
|
30
|
+
|
31
|
+
# This turns the arguments string and other options into a nicely formatted
|
32
|
+
# hash.
|
33
|
+
#
|
34
|
+
# @return [Array<Hash>]
|
35
|
+
def to_a
|
36
|
+
multiple = to_arrays(@opts.dup)
|
37
|
+
args = split_into_hashes(multiple)
|
38
|
+
|
39
|
+
if @opts.has_key?(:arg)
|
40
|
+
# Parse the argument string and merge in previous options from +singles+.
|
41
|
+
args = parse_args_string(args, @opts[:arg])
|
42
|
+
end
|
43
|
+
|
44
|
+
infer_args(args)
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [Array<Argument>]
|
48
|
+
def to_args
|
49
|
+
to_a.map do |arg|
|
50
|
+
Argument.new(arg.delete(:name) || 'arg', arg)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# Infer arguments that haven't been explicitly defined by name. This allows you
|
57
|
+
# to just say "it" should be within the range +1..5+ and have an argument
|
58
|
+
# created without having to pass +:arg => '<choice>'+.
|
59
|
+
def infer_args(opts)
|
60
|
+
opts.map do |hash|
|
61
|
+
if hash.has_key?(:name)
|
62
|
+
hash
|
63
|
+
else
|
64
|
+
check = [:type, :match, :constraint, :within, :default]
|
65
|
+
if check.any? {|key| hash.has_key?(key) }
|
66
|
+
hash.merge! :name => 'arg'
|
67
|
+
end
|
68
|
+
|
69
|
+
if hash.has_key?(:default) && !hash.has_key?(:optional)
|
70
|
+
hash.merge! :optional => true
|
71
|
+
end
|
72
|
+
|
73
|
+
hash
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# @param opts [Hash] Hash to rename keys in
|
79
|
+
# @param keys [Hash] Map of key names to desired key names
|
80
|
+
#
|
81
|
+
# @example
|
82
|
+
#
|
83
|
+
# normalise_key_names({:a => 1, :b => 2}, {:a => :b, :b => :c})
|
84
|
+
# #=> {:b => 1, :c => 2}
|
85
|
+
#
|
86
|
+
def normalise_key_names(opts, keys)
|
87
|
+
opts.inject({}) do |hsh, (k,v)|
|
88
|
+
hsh[keys[k]] = v if keys.has_key?(k)
|
89
|
+
hsh
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Adds enough of +pd+ to +obj+ to make it have #size of +max+.
|
94
|
+
#
|
95
|
+
# @param obj [#size]
|
96
|
+
# @param max [Integer]
|
97
|
+
# @param pd [Object]
|
98
|
+
# @return [Object]
|
99
|
+
def pad(obj, max, pd=nil)
|
100
|
+
i = (max - obj.size)
|
101
|
+
obj + [pd] * (i < 0 ? 0 : i)
|
102
|
+
end
|
103
|
+
|
104
|
+
# @param name [String]
|
105
|
+
# @return [String] Argument name without sqaure or angle brackets
|
106
|
+
def clean(name)
|
107
|
+
name.tr '[<>]', ''
|
108
|
+
end
|
109
|
+
|
110
|
+
# @param hash [Hash<Symbol=>Object, Symbol=>Array>]
|
111
|
+
# @return [Hash<Symbol=>Array>]
|
112
|
+
def to_arrays(hash)
|
113
|
+
# :within is weird. You will generally set it to an Array, but can use
|
114
|
+
# anything which responds to #include?. Unfortunately that includes String
|
115
|
+
# which for many reasons should be checked against. So new rules...
|
116
|
+
#
|
117
|
+
# hash[:within] = <#include?>
|
118
|
+
# #=> hash[:within] = [<#include?>]
|
119
|
+
#
|
120
|
+
# hash[:within] = [<#include?>]
|
121
|
+
# #=> hash[:within] = [<#include?>]
|
122
|
+
#
|
123
|
+
# hash[:within] = '1'..'5'
|
124
|
+
# #=> hash[:within] = ['1'..'5']
|
125
|
+
#
|
126
|
+
# hash[:type] = Integer
|
127
|
+
# hash[:within] = 1..5
|
128
|
+
# #=> hash[:within] = [1..5]
|
129
|
+
#
|
130
|
+
# hash[:type] = Integer
|
131
|
+
# hash[:within] = [1..5, nil]
|
132
|
+
# #=> hash[:within] = [1..5, nil]
|
133
|
+
#
|
134
|
+
if hash[:within].respond_to?(:[]) && hash[:within].respond_to?(:include?)
|
135
|
+
if hash[:within].all? {|o|
|
136
|
+
([String] << hash[:type]).flatten.uniq.compact.any? {|t|
|
137
|
+
o.is_a?(t)
|
138
|
+
}
|
139
|
+
}
|
140
|
+
hash[:within] = [hash[:within]].compact
|
141
|
+
|
142
|
+
elsif hash[:within].any? {|o| o.respond_to?(:include?) }
|
143
|
+
hash[:within] = hash[:within]
|
144
|
+
|
145
|
+
else
|
146
|
+
hash[:within] = [hash[:within]].compact
|
147
|
+
end
|
148
|
+
else
|
149
|
+
hash[:within] = [hash[:within]].compact
|
150
|
+
end
|
151
|
+
|
152
|
+
# Make all the values Arrays
|
153
|
+
Hash[ hash.map {|k,v| [k, Array(v)] } ]
|
154
|
+
end
|
155
|
+
|
156
|
+
# Splits a single hash of arrays into a single array of hashes.
|
157
|
+
#
|
158
|
+
# @example
|
159
|
+
#
|
160
|
+
# hash = {:a => [:g, :h, :i], :b => [:x, :y]}
|
161
|
+
# split_into_hashes(hash)
|
162
|
+
# #=> [
|
163
|
+
# # {:a => :b, :b => :x},
|
164
|
+
# # {:a => :h, :b => :y},
|
165
|
+
# # {:a => :i}
|
166
|
+
# # ]
|
167
|
+
#
|
168
|
+
def split_into_hashes(hash)
|
169
|
+
# Find the largest Array...
|
170
|
+
max = hash.values.map(&:size).max || 0
|
171
|
+
|
172
|
+
hash.map {|k, arr| pad(arr, max).map {|i| [k, i] } }.
|
173
|
+
transpose.
|
174
|
+
map {|i| Hash[ i.reject {|a,b| b == nil || a == :arg } ] }
|
175
|
+
end
|
176
|
+
|
177
|
+
# Parses the string passed in as +:arg+. The string should have the
|
178
|
+
# following format:
|
179
|
+
#
|
180
|
+
# <arg-name> - Indicates a required argument called "arg-name"
|
181
|
+
# [...] - Can surround one or more <arg> tokens and means they are
|
182
|
+
# optional, eg. "[<optional> <and-another>]"
|
183
|
+
#
|
184
|
+
# @param hash [Hash<Symbol=>Object>]
|
185
|
+
# @param arg_str [String]
|
186
|
+
def parse_args_string(hash, arg_str)
|
187
|
+
optional = false
|
188
|
+
cancelled_optional = false
|
189
|
+
|
190
|
+
arg_str.split(' ').zip(hash).map do |arg, opts|
|
191
|
+
if cancelled_optional
|
192
|
+
optional = false
|
193
|
+
cancelled_optional = false
|
194
|
+
end
|
195
|
+
|
196
|
+
cancelled_optional = true if arg[-1..-1] == ']'
|
197
|
+
|
198
|
+
if arg[0..0] == '['
|
199
|
+
optional = true
|
200
|
+
elsif arg[0..0] != '<'
|
201
|
+
raise InvalidArgumentStringError.new(opts[:arg])
|
202
|
+
end
|
203
|
+
|
204
|
+
{:name => clean(arg), :optional => optional}.merge(opts || {})
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
data/lib/clive/base.rb
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
class Clive
|
2
|
+
class Base < Command
|
3
|
+
|
4
|
+
attr_reader :commands
|
5
|
+
|
6
|
+
DEFAULTS = {
|
7
|
+
:name => File.basename($0),
|
8
|
+
:formatter => Formatter::Colour.new,
|
9
|
+
:help_command => true,
|
10
|
+
:help => true
|
11
|
+
}
|
12
|
+
|
13
|
+
# These options should be copied into each {Command} that is created.
|
14
|
+
GLOBAL_OPTIONS = [:name, :formatter, :help]
|
15
|
+
|
16
|
+
# You don't need to create an instance of this, create a class extending
|
17
|
+
# Clive or call Clive.new instead.
|
18
|
+
#
|
19
|
+
# @param config [Hash] Options to set for this Base, see #run for details
|
20
|
+
# of the keys that can be passed.
|
21
|
+
def initialize(config={}, &block)
|
22
|
+
super([], config, &block)
|
23
|
+
|
24
|
+
@commands = []
|
25
|
+
@header = proc { "Usage: #{@config[:name]} [command] [options]" }
|
26
|
+
@footer = ""
|
27
|
+
@_group = nil
|
28
|
+
@config = DEFAULTS.merge(get_subhash(config, DEFAULTS.keys))
|
29
|
+
|
30
|
+
# Need to keep a state before #run is called so #set works.
|
31
|
+
@state = {}
|
32
|
+
instance_exec &block if block
|
33
|
+
end
|
34
|
+
|
35
|
+
# Runs the Clive with the args passed which defaults to +ARGV+.
|
36
|
+
#
|
37
|
+
# @param args [Array<String>]
|
38
|
+
# Command line arguments to run with
|
39
|
+
#
|
40
|
+
# @param config [Hash]
|
41
|
+
# @option config [Boolean] :help
|
42
|
+
# Whether to create a help option.
|
43
|
+
# @option config [Boolean] :help_command
|
44
|
+
# Whether to create a help command.
|
45
|
+
# @option config [Formatter] :formatter
|
46
|
+
# Help formatter to use.
|
47
|
+
# @option config [String] :name
|
48
|
+
# Name to use in headers, this is usually better than setting a header as
|
49
|
+
# commands will use this to generate their own headers for use in help.
|
50
|
+
#
|
51
|
+
def run(args=ARGV, config={})
|
52
|
+
@config = @config.merge(get_subhash(config, DEFAULTS.keys))
|
53
|
+
|
54
|
+
add_help_option
|
55
|
+
add_help_command
|
56
|
+
|
57
|
+
Clive::Parser.new(self, config).parse(args, @state)
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
# @group DSL Methods
|
62
|
+
|
63
|
+
# @overload command(*names, description=current_desc, opts={}, &block)
|
64
|
+
# Creates a new Command. @param names [Array<Symbol>] Names that the
|
65
|
+
# command can be called with. @param description [String] Description of
|
66
|
+
# the command. @param opts [Hash] Options to be passed to the new
|
67
|
+
# Command, see {Command#initialize}.
|
68
|
+
#
|
69
|
+
# @example
|
70
|
+
#
|
71
|
+
# class CLI
|
72
|
+
# desc 'Creates a new thing'
|
73
|
+
# command :create, arg: '<thing>' do
|
74
|
+
# # ...
|
75
|
+
# end
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
def command(*args, &block)
|
79
|
+
ns, d, o = [], current_desc, {}
|
80
|
+
args.each do |i|
|
81
|
+
case i
|
82
|
+
when ::Symbol then ns << i
|
83
|
+
when ::String then d = i
|
84
|
+
when ::Hash then o = i
|
85
|
+
end
|
86
|
+
end
|
87
|
+
o = DEFAULTS.merge(Hash[global_opts]).merge(o)
|
88
|
+
@commands << Command.new(ns, d, o.merge({:group => @_group}), &block)
|
89
|
+
end
|
90
|
+
|
91
|
+
include Clive::StateActions
|
92
|
+
|
93
|
+
# Set configuration values for the base, as if you passed an options hash
|
94
|
+
# to #initialize.
|
95
|
+
#
|
96
|
+
# @param [Hash] See #initialize
|
97
|
+
# @example
|
98
|
+
#
|
99
|
+
# config arg: '<dir>'
|
100
|
+
#
|
101
|
+
def config(opts=nil)
|
102
|
+
if opts
|
103
|
+
@config = @config.merge(get_subhash(opts, DEFAULTS.keys))
|
104
|
+
else
|
105
|
+
@config
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# @endgroup
|
110
|
+
|
111
|
+
# Finds the option or command represented by +arg+, this can the name of a command
|
112
|
+
# or an option which should include the correct number of dashes. If the option or
|
113
|
+
# command cannot be found +nil+ is returned.
|
114
|
+
#
|
115
|
+
# @param arg [String]
|
116
|
+
# @see Command#find
|
117
|
+
# @example
|
118
|
+
#
|
119
|
+
# c = Clive.new {
|
120
|
+
# command :new
|
121
|
+
# opt :v, :version
|
122
|
+
# }
|
123
|
+
#
|
124
|
+
# c.find('-v')
|
125
|
+
# #=> #<Clive::Option -v, --version>
|
126
|
+
# c.find('new')
|
127
|
+
# #=> #<Clive::Command new>
|
128
|
+
# c.find('test')
|
129
|
+
# #=> nil
|
130
|
+
#
|
131
|
+
def find(arg)
|
132
|
+
if arg[0..0] == '-'
|
133
|
+
super
|
134
|
+
else
|
135
|
+
find_command(arg.to_sym)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Finds the command with the name given, if the command cannot be found
|
140
|
+
# returns +nil+.
|
141
|
+
#
|
142
|
+
# @param arg [Symbol, nil]
|
143
|
+
# @example
|
144
|
+
#
|
145
|
+
# c = Clive.new {
|
146
|
+
# command :new
|
147
|
+
# }
|
148
|
+
#
|
149
|
+
# c.find_command(:new)
|
150
|
+
# #=> #<Clive::Command new>
|
151
|
+
#
|
152
|
+
def find_command(arg)
|
153
|
+
@commands.find {|i| i.names.include?(arg) }
|
154
|
+
end
|
155
|
+
|
156
|
+
# Attempts to find the command with the name given, returns true if the
|
157
|
+
# command exits.
|
158
|
+
#
|
159
|
+
# @param arg [Symbol]
|
160
|
+
# @example
|
161
|
+
#
|
162
|
+
# c = Clive.new {
|
163
|
+
# command :new
|
164
|
+
# }
|
165
|
+
#
|
166
|
+
# c.has_command? :new #=> true
|
167
|
+
# c.has_command? :create #=> false
|
168
|
+
#
|
169
|
+
def has_command?(arg)
|
170
|
+
!!find_command(arg)
|
171
|
+
end
|
172
|
+
|
173
|
+
private
|
174
|
+
|
175
|
+
# Options which should be copied into each Command created.
|
176
|
+
def global_opts
|
177
|
+
@config.find_all {|k,v| GLOBAL_OPTIONS.include?(k) }
|
178
|
+
end
|
179
|
+
|
180
|
+
# Adds the help command, which accepts the name of a command to display help
|
181
|
+
# for, to this if it is wanted.
|
182
|
+
def add_help_command
|
183
|
+
if @config[:help] && @config[:help_command] && !has_command?(:help)
|
184
|
+
self.command(:help, 'Display help', :arg => '[<command>]', :tail => true)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
end
|
data/lib/clive/command.rb
CHANGED
@@ -1,510 +1,408 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
# A
|
4
|
-
#
|
5
|
-
#
|
1
|
+
class Clive
|
2
|
+
|
3
|
+
# A command allows you to separate groups of commands under their own
|
4
|
+
# namespace. But it can also take arguments like an Option. Instead of
|
5
|
+
# executing the block passed to it executes the block passed to {#action}.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
#
|
9
|
+
# class CLI < Clive
|
10
|
+
#
|
11
|
+
# command :new, arg: '<dir>' do
|
12
|
+
# # options
|
13
|
+
# bool :force
|
14
|
+
#
|
15
|
+
# action do |dir|
|
16
|
+
# # code
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# end
|
6
21
|
#
|
7
22
|
class Command < Option
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
# @
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
@
|
58
|
-
@
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
@
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
# @return [Array] all flags in this command
|
75
|
-
def flags
|
76
|
-
@options.find_all {|i| i.class == Flag}
|
23
|
+
|
24
|
+
# @return [Array<Option>] List of options created in the Command instance
|
25
|
+
attr_reader :options
|
26
|
+
|
27
|
+
DEFAULTS = {
|
28
|
+
:group => nil,
|
29
|
+
:head => false,
|
30
|
+
:tail => false,
|
31
|
+
:runner => Clive::Option::Runner,
|
32
|
+
|
33
|
+
# these two are copied in from Base, so will be merged over
|
34
|
+
:formatter => nil,
|
35
|
+
:help => nil,
|
36
|
+
:name => nil
|
37
|
+
}
|
38
|
+
|
39
|
+
# @param names [Array[Symbol]]
|
40
|
+
# Names that the Command can be ran with.
|
41
|
+
#
|
42
|
+
# @param desc [String]
|
43
|
+
# Description of the Command, this is shown in help and will be wrapped properly.
|
44
|
+
#
|
45
|
+
# @param config [Hash]
|
46
|
+
# @option config [Boolean] :head
|
47
|
+
# If option should be at top of help list.
|
48
|
+
# @option config [Boolean] :tail
|
49
|
+
# If option should be at bottom of help list.
|
50
|
+
# @option config [String] :group
|
51
|
+
# Name of the group this option belongs to. This is actually set when
|
52
|
+
# {Command#group} is used.
|
53
|
+
# @option config [Runner] :runner
|
54
|
+
# Class to use for running the block passed to #action. This doesn't have
|
55
|
+
# to be Option::Runner, but you probably never need to change this.
|
56
|
+
# @option config [Formatter] :formatter
|
57
|
+
# Help formatter to use for this command, defaults to top-level formatter.
|
58
|
+
# @option config [Boolean] :help
|
59
|
+
# Whether to add a '-h, --help' option to this command which displays help.
|
60
|
+
# @option config [String] :args
|
61
|
+
# Arguments that the option takes. See {Argument}.
|
62
|
+
# @option config [Type, Array[Type]] :as
|
63
|
+
# The class the argument(s) should be cast to. See {Type}.
|
64
|
+
# @option config [#match, Array[#match]] :match
|
65
|
+
# Regular expression that the argument(s) must match.
|
66
|
+
# @option config [#include?, Array[#include?]] :in
|
67
|
+
# Collection that argument(s) must be in.
|
68
|
+
# @option config [Object] :default
|
69
|
+
# Default value that is used if argument is not given.
|
70
|
+
#
|
71
|
+
def initialize(names=[], description="", config={}, &block)
|
72
|
+
@names = names
|
73
|
+
@description = description
|
74
|
+
@options = []
|
75
|
+
@_block = block
|
76
|
+
|
77
|
+
@args = Arguments.create(get_subhash(config, Arguments::Parser::KEYS))
|
78
|
+
@config = DEFAULTS.merge(get_subhash(config, DEFAULTS.keys))
|
79
|
+
|
80
|
+
# Create basic header "Usage: filename commandname(s) [options]
|
81
|
+
@header = proc { "Usage: #{@config[:name]} #{to_s} [options]" }
|
82
|
+
@footer = ""
|
83
|
+
@_group = nil
|
84
|
+
|
85
|
+
add_help_option
|
86
|
+
|
87
|
+
current_desc
|
77
88
|
end
|
78
|
-
|
79
|
-
#
|
80
|
-
#
|
81
|
-
|
82
|
-
|
83
|
-
# when called.
|
84
|
-
#
|
85
|
-
def find
|
86
|
-
return nil if @base || @block.nil?
|
87
|
-
self.instance_eval(&@block)
|
88
|
-
@block = nil
|
89
|
+
|
90
|
+
# @return [Symbol] Single name to use when referring specifically to this command.
|
91
|
+
# Use the first name that was passed in.
|
92
|
+
def name
|
93
|
+
names.first
|
89
94
|
end
|
90
|
-
|
91
|
-
#
|
92
|
-
|
93
|
-
|
94
|
-
# @return [Constant]
|
95
|
-
#
|
96
|
-
def type_is?(name)
|
97
|
-
find_opt(name).class.name || Clive::Command
|
95
|
+
|
96
|
+
# @return [String]
|
97
|
+
def to_s
|
98
|
+
names.join(',')
|
98
99
|
end
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
100
|
+
|
101
|
+
# Runs the block that was given to {Command#initialize} within the context of the
|
102
|
+
# command. The state hash is passed (and returned) so that {#set} works outside
|
103
|
+
# of {Runner} allowing default values to be set.
|
104
|
+
#
|
105
|
+
# @param state [Hash] The newly created state for the command.
|
106
|
+
# @return [Hash] The returned hash is used for the state of the command.
|
107
|
+
def run_block(state)
|
108
|
+
if @_block
|
109
|
+
@state = state
|
110
|
+
instance_exec(&@_block)
|
111
|
+
@state
|
110
112
|
else
|
111
|
-
|
113
|
+
@state = state
|
112
114
|
end
|
113
115
|
end
|
114
|
-
|
115
|
-
#
|
116
|
+
|
117
|
+
# @group DSL Methods
|
118
|
+
|
119
|
+
# Set the header for {#help}.
|
120
|
+
# @param [String]
|
121
|
+
# @example
|
116
122
|
#
|
117
|
-
#
|
118
|
-
# @return [Clive::Option]
|
123
|
+
# header 'Usage: my_app [options] [args]'
|
119
124
|
#
|
120
|
-
def
|
121
|
-
|
125
|
+
def header(val)
|
126
|
+
@header = val
|
122
127
|
end
|
123
|
-
|
124
|
-
#
|
128
|
+
|
129
|
+
# Set the footer for {#help}.
|
130
|
+
# @param [String]
|
131
|
+
# @example
|
125
132
|
#
|
126
|
-
#
|
127
|
-
# @return [true, false]
|
133
|
+
# footer 'For more help visit http://mysite.com/help'
|
128
134
|
#
|
129
|
-
def
|
130
|
-
|
135
|
+
def footer(val)
|
136
|
+
@footer = val
|
131
137
|
end
|
132
|
-
|
133
|
-
#
|
138
|
+
|
139
|
+
# Set configuration values for the command, as if you passed an options hash
|
140
|
+
# to #initialize.
|
134
141
|
#
|
135
|
-
# @param
|
136
|
-
# @
|
142
|
+
# @param [Hash] See #initialize
|
143
|
+
# @example
|
144
|
+
#
|
145
|
+
# config arg: '<dir>'
|
137
146
|
#
|
138
|
-
def
|
139
|
-
|
147
|
+
def config(opts=nil)
|
148
|
+
if opts
|
149
|
+
@config = @config.merge(get_subhash(opts, DEFAULTS.keys))
|
150
|
+
else
|
151
|
+
@config
|
152
|
+
end
|
140
153
|
end
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
#
|
154
|
+
|
155
|
+
include Clive::StateActions
|
156
|
+
|
157
|
+
# @overload option(short=nil, long=nil, description=current_desc, opts={}, &block)
|
158
|
+
# Creates a new Option in the Command. Either +short+ or +long+ must be set.
|
159
|
+
# @param short [Symbol] The short name for the option (:a would become +-a+)
|
160
|
+
# @param long [Symbol] The long name for the option (:add would become +--add+)
|
161
|
+
# @param description [String] Description of the option
|
162
|
+
# @param opts [Hash] Options to create the Option with, see {Option#initialize}
|
145
163
|
#
|
146
164
|
# @example
|
147
165
|
#
|
148
|
-
#
|
149
|
-
#
|
150
|
-
#
|
151
|
-
#
|
152
|
-
#
|
153
|
-
#
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
result << [:option, i]
|
164
|
-
end
|
165
|
-
|
166
|
-
else
|
167
|
-
result << [:word, a]
|
166
|
+
# opt :type, arg: '<size>', in: %w(small medium large) do
|
167
|
+
# case size
|
168
|
+
# when "small" then set(:size, 1)
|
169
|
+
# when "medium" then set(:size, 2)
|
170
|
+
# when "large" then set(:size, 3)
|
171
|
+
# end
|
172
|
+
# end
|
173
|
+
#
|
174
|
+
def option(*args, &block)
|
175
|
+
ns, d, o = [], current_desc, {}
|
176
|
+
args.each do |i|
|
177
|
+
case i
|
178
|
+
when ::Symbol then ns << i
|
179
|
+
when ::String then d = i
|
180
|
+
when ::Hash then o = i
|
168
181
|
end
|
169
182
|
end
|
170
|
-
|
171
|
-
result
|
183
|
+
@options << Option.new(ns, d, ({:group => @_group}).merge(o), &block)
|
172
184
|
end
|
173
|
-
|
174
|
-
|
175
|
-
#
|
185
|
+
alias_method :opt, :option
|
186
|
+
|
187
|
+
# @overload boolean(short=nil, long, description=current_desc, opts={}, &block)
|
188
|
+
# Creates a new Option in the Command which responds to calls with a 'no-' prefix.
|
189
|
+
# +long+ must be set.
|
190
|
+
# @param short [Symbol] The short name for the option (:a would become +-a+)
|
191
|
+
# @param long [Symbol] The long name for the option (:add would become +--add+)
|
192
|
+
# @param description [String] Description of the option
|
193
|
+
# @param opts [Hash] Options to create the Option with, see {Option#initialize}
|
176
194
|
#
|
177
195
|
# @example
|
178
|
-
#
|
179
|
-
#
|
180
|
-
#
|
181
|
-
# #
|
182
|
-
# #
|
183
|
-
# #
|
184
|
-
#
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
def tokens_to_tree(arr)
|
193
|
-
tree = []
|
194
|
-
self.find
|
195
|
-
|
196
|
-
l = arr.size
|
197
|
-
i = 0
|
198
|
-
while i < l
|
199
|
-
a = arr[i]
|
200
|
-
|
201
|
-
if a[0] == :word
|
202
|
-
|
203
|
-
last = tree.last || []
|
204
|
-
|
205
|
-
if last[0] == :flag
|
206
|
-
last[2] ||= []
|
207
|
-
end
|
208
|
-
|
209
|
-
if command = find_command(a[1])
|
210
|
-
if last[0] == :flag
|
211
|
-
if last[2].size < last[1].arg_size(:mandatory)
|
212
|
-
last[2] << [:arg, a[1]]
|
213
|
-
else
|
214
|
-
tree << [:command, command, command.tokens_to_tree(arr[i+1..-1])]
|
215
|
-
i = l
|
216
|
-
end
|
217
|
-
else
|
218
|
-
tree << [:command, command, command.tokens_to_tree(arr[i+1..-1])]
|
219
|
-
i = l
|
220
|
-
end
|
221
|
-
else
|
222
|
-
if last[0] == :flag && last[2].size < last[1].arg_size(:all)
|
223
|
-
last[2] << [:arg, a[1]]
|
224
|
-
else
|
225
|
-
tree << [:arg, a[1]]
|
226
|
-
end
|
227
|
-
end
|
228
|
-
else
|
229
|
-
tree << [opt_type(a[1]), find_opt(a[1])]
|
230
|
-
end
|
231
|
-
|
232
|
-
i += 1
|
233
|
-
end
|
234
|
-
|
235
|
-
tree
|
236
|
-
end
|
237
|
-
|
238
|
-
# Traverses the tree created by #tokens_to_tree and runs the correct options.
|
239
|
-
#
|
240
|
-
# @param tree [Array]
|
241
|
-
# @return [Array]
|
242
|
-
# Any unused arguments.
|
243
|
-
#
|
244
|
-
def run_tree(tree)
|
245
|
-
i = 0
|
246
|
-
l = tree.size
|
247
|
-
r = []
|
248
|
-
|
249
|
-
while i < l
|
250
|
-
curr = tree[i]
|
251
|
-
|
252
|
-
case curr[0]
|
253
|
-
when :command
|
254
|
-
r << curr[1].run(curr[2])
|
255
|
-
|
256
|
-
when :switch
|
257
|
-
curr[1].run
|
258
|
-
|
259
|
-
when :flag
|
260
|
-
args = curr[2].map {|i| i[1] }
|
261
|
-
if args.size < curr[1].arg_size(:mandatory)
|
262
|
-
raise MissingArgument.new(curr[1].sort_name)
|
263
|
-
end
|
264
|
-
curr[1].run(args)
|
265
|
-
|
266
|
-
when :arg
|
267
|
-
r << curr[1]
|
196
|
+
#
|
197
|
+
# bool :auto, 'Auto regenerate on changes'
|
198
|
+
#
|
199
|
+
# # Usage
|
200
|
+
# # --auto sets :auto to true
|
201
|
+
# # --no-auto sets :auto to false
|
202
|
+
#
|
203
|
+
def boolean(*args, &block)
|
204
|
+
ns, d, o = [], current_desc, {}
|
205
|
+
args.each do |i|
|
206
|
+
case i
|
207
|
+
when ::Symbol then ns << i
|
208
|
+
when ::String then d = i
|
209
|
+
when ::Hash then o = i
|
268
210
|
end
|
269
|
-
|
270
|
-
i += 1
|
271
211
|
end
|
272
|
-
|
212
|
+
@options << Option.new(ns, d, ({:group => @_group, :boolean => true}).merge(o), &block)
|
273
213
|
end
|
274
|
-
|
275
|
-
|
276
|
-
#
|
214
|
+
alias_method :bool, :boolean
|
215
|
+
|
216
|
+
# If an argument is given it will set the description to that, otherwise it will
|
217
|
+
# return the description for the command.
|
218
|
+
#
|
219
|
+
# @param arg [String]
|
220
|
+
# @example
|
277
221
|
#
|
278
|
-
#
|
279
|
-
#
|
222
|
+
# description 'Displays the current version'
|
223
|
+
# opt(:version) { puts $VERSION }
|
280
224
|
#
|
281
|
-
def
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
run_tree(to_run)
|
287
|
-
end
|
288
|
-
|
289
|
-
|
290
|
-
def to_h
|
291
|
-
{
|
292
|
-
'names' => @names,
|
293
|
-
'desc' => @desc
|
294
|
-
}
|
295
|
-
end
|
296
|
-
|
297
|
-
def method_missing(sym, *args, &block)
|
298
|
-
if @top_klass.respond_to?(sym)
|
299
|
-
@top_klass.send(sym, *args)
|
225
|
+
def description(arg=nil)
|
226
|
+
if arg
|
227
|
+
@_last_desc = arg
|
228
|
+
else
|
229
|
+
@description
|
300
230
|
end
|
301
231
|
end
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
# @group DSL
|
306
|
-
|
307
|
-
# Add a new command to +@commands+
|
308
|
-
#
|
309
|
-
# @overload command(name, ..., desc, &block)
|
310
|
-
# Creates a new command
|
311
|
-
# @param [Symbol] name the name(s) of the command, eg. +:add+ for +git add+
|
312
|
-
# @param [String] desc description of the command
|
313
|
-
#
|
314
|
-
# @yield A block to run when the command is called, can contain switches
|
315
|
-
# and flags
|
316
|
-
#
|
317
|
-
def command(*args, &block)
|
318
|
-
@commands << Command.new(args, @current_desc, @top_klass, &block)
|
319
|
-
@current_desc = ""
|
320
|
-
end
|
321
|
-
|
322
|
-
# Add a new switch to +@switches+
|
323
|
-
# @see Switch#initialize
|
324
|
-
def switch(*args, &block)
|
325
|
-
@options << Switch.new(args, @current_desc, &block)
|
326
|
-
@current_desc = ""
|
327
|
-
end
|
328
232
|
|
329
|
-
#
|
330
|
-
#
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
arg = i[:arg]
|
340
|
-
else
|
341
|
-
arg = i[:args]
|
342
|
-
end
|
343
|
-
end
|
344
|
-
end
|
345
|
-
@options << Flag.new(names, @current_desc, arg, &block)
|
346
|
-
@current_desc = ""
|
347
|
-
end
|
348
|
-
|
349
|
-
# Creates a boolean switch. This is done by adding two switches of
|
350
|
-
# Bool type to +@switches+, one is created normally the other has
|
351
|
-
# "no-" appended to the long name and has no short name.
|
352
|
-
#
|
353
|
-
# @see Bool#initialize
|
354
|
-
def bool(*args, &block)
|
355
|
-
@options << Bool.new(args, @current_desc, true, &block)
|
356
|
-
@options << Bool.new(args, @current_desc, false, &block)
|
357
|
-
@current_desc= ""
|
233
|
+
# Short version of {#description} which can only set.
|
234
|
+
#
|
235
|
+
# @param arg [String]
|
236
|
+
# @example
|
237
|
+
#
|
238
|
+
# desc 'Displays the current version'
|
239
|
+
# opt(:version) { puts $VERSION }
|
240
|
+
#
|
241
|
+
def desc(arg)
|
242
|
+
@_last_desc = arg
|
358
243
|
end
|
359
|
-
|
360
|
-
#
|
361
|
-
#
|
244
|
+
|
245
|
+
# The action block is the block which will be executed with any arguments that
|
246
|
+
# are found for it. It sets +@block+ so that {Option#run} does not have to be redefined.
|
362
247
|
#
|
363
248
|
# @example
|
364
249
|
#
|
365
|
-
#
|
366
|
-
#
|
250
|
+
# command :create, arg: '<name>', 'Creates a new project' do
|
251
|
+
# bool :bare, "Don't add boilerplate code to created files"
|
367
252
|
#
|
368
|
-
#
|
369
|
-
#
|
370
|
-
#
|
253
|
+
# action do |name|
|
254
|
+
# if get(:bare)
|
255
|
+
# # write some empty files
|
256
|
+
# else
|
257
|
+
# # create some files with stuff in
|
258
|
+
# end
|
371
259
|
# end
|
372
260
|
# end
|
373
261
|
#
|
374
|
-
def
|
375
|
-
|
376
|
-
@current_desc = str
|
377
|
-
else
|
378
|
-
@desc
|
379
|
-
end
|
262
|
+
def action(&block)
|
263
|
+
@block = block
|
380
264
|
end
|
381
|
-
|
382
|
-
#
|
265
|
+
|
266
|
+
# Set the group name for all options defined after it.
|
383
267
|
#
|
268
|
+
# @param name [String]
|
384
269
|
# @example
|
385
270
|
#
|
386
|
-
#
|
387
|
-
#
|
271
|
+
# group 'Files'
|
272
|
+
# opt :move, 'Moves a file', args: '<from> <to>'
|
273
|
+
# opt :delete, 'Deletes a file', arg: '<file>'
|
274
|
+
# opt :create, 'Creates a file', arg: '<name>'
|
388
275
|
#
|
389
|
-
#
|
390
|
-
#
|
391
|
-
#
|
276
|
+
# group 'Network'
|
277
|
+
# opt :upload, 'Uploads everything'
|
278
|
+
# opt :download, 'Downloads everyhting'
|
392
279
|
#
|
393
|
-
def
|
394
|
-
@
|
395
|
-
end
|
396
|
-
|
397
|
-
# Set the header
|
398
|
-
def header(val)
|
399
|
-
@header = val
|
400
|
-
end
|
401
|
-
|
402
|
-
# Set the footer
|
403
|
-
def footer(val)
|
404
|
-
@footer = val
|
405
|
-
end
|
406
|
-
|
407
|
-
# @group Help
|
408
|
-
|
409
|
-
# This actually creates a switch with "-h" and "--help" that controls
|
410
|
-
# the help on this command.
|
411
|
-
def build_help
|
412
|
-
@options << Switch.new([:h, :help], "Display help") do
|
413
|
-
puts self.help
|
414
|
-
exit 0
|
415
|
-
end
|
280
|
+
def group(name)
|
281
|
+
@_group = name
|
416
282
|
end
|
417
283
|
|
418
|
-
#
|
419
|
-
|
420
|
-
|
421
|
-
def help
|
422
|
-
@formatter.format(@header, @footer, @commands, @options)
|
284
|
+
# Sugar for +group(nil)+
|
285
|
+
def end_group
|
286
|
+
group nil
|
423
287
|
end
|
424
|
-
|
425
|
-
#
|
288
|
+
|
289
|
+
# @endgroup
|
290
|
+
|
291
|
+
# Finds the option represented by +arg+, this can either be the long name +--opt+
|
292
|
+
# or the short name +-o+, if the option can't be found +nil+ is returned.
|
426
293
|
#
|
427
|
-
#
|
428
|
-
#
|
429
|
-
#
|
294
|
+
# @param arg [String]
|
295
|
+
# @return [Option, nil]
|
296
|
+
# @example
|
430
297
|
#
|
431
|
-
#
|
432
|
-
#
|
433
|
-
#
|
434
|
-
# * desc - a string of the description for the option
|
298
|
+
# a = Command.new [:command] do
|
299
|
+
# bool :force
|
300
|
+
# end
|
435
301
|
#
|
436
|
-
#
|
302
|
+
# a.find('--force')
|
303
|
+
# #=> #<Clive::Option --[no-]force>
|
437
304
|
#
|
438
|
-
|
439
|
-
|
305
|
+
def find(arg)
|
306
|
+
if arg[0..1] == '--'
|
307
|
+
find_option(arg[2..-1].gsub('-', '_').to_sym)
|
308
|
+
elsif arg[0..0] == '-'
|
309
|
+
find_option(arg[1..-1].to_sym)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
alias_method :[], :find
|
313
|
+
|
314
|
+
# Attempts to find the option represented by the string +arg+, returns true if
|
315
|
+
# it exists and false if not.
|
440
316
|
#
|
317
|
+
# @param arg [String]
|
318
|
+
# @example
|
441
319
|
#
|
442
|
-
#
|
443
|
-
#
|
444
|
-
#
|
445
|
-
#
|
446
|
-
# @option args [Integer] :prepend Width of spaces to prepend with
|
320
|
+
# a = Command.new [:command] do
|
321
|
+
# bool :force
|
322
|
+
# bool :auto
|
323
|
+
# end
|
447
324
|
#
|
448
|
-
#
|
449
|
-
#
|
450
|
-
#
|
325
|
+
# a.has?('--force') #=> true
|
326
|
+
# a.has?('--auto') #=> true
|
327
|
+
# a.has?('--no-auto') #=> false
|
328
|
+
# a.has?('--not-real') #=> false
|
451
329
|
#
|
330
|
+
def has?(arg)
|
331
|
+
!!find(arg)
|
332
|
+
end
|
333
|
+
|
334
|
+
# Finds the option with the name given by +arg+, this must be in Symbol form so
|
335
|
+
# does not have a dash before it. As with {#find} if the option does not exist +nil+
|
336
|
+
# will be returned.
|
452
337
|
#
|
338
|
+
# @param arg [Symbol]
|
339
|
+
# @return [Option, nil]
|
453
340
|
# @example
|
454
|
-
#
|
455
|
-
# CLI.help_formatter do |h|
|
456
|
-
#
|
457
|
-
# h.switch "{prepend}{names.join(', ')} {spaces}{desc.grey}"
|
458
|
-
# h.bool "{prepend}{names.join(', ')} {spaces}{desc.grey}"
|
459
|
-
# h.flag "{prepend}{names.join(', ')} {args.join(' ')} {spaces}{desc.grey}"
|
460
|
-
# h.command "{prepend}{names.join(', ')} {spaces}{desc.grey}"
|
461
341
|
#
|
342
|
+
# a = Command.new [:command] do
|
343
|
+
# bool :force
|
462
344
|
# end
|
463
|
-
#
|
464
|
-
#
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
345
|
+
#
|
346
|
+
# a.find_option(:force)
|
347
|
+
# #=> #<Clive::Option --[no-]force>
|
348
|
+
#
|
349
|
+
def find_option(arg)
|
350
|
+
@options.find {|opt| opt.names.include?(arg) }
|
351
|
+
end
|
352
|
+
|
353
|
+
# Attempts to find the option with the Symbol name given, returns true if the option
|
354
|
+
# exists and false if not.
|
355
|
+
#
|
356
|
+
# @param arg [Symbol]
|
357
|
+
def has_option?(arg)
|
358
|
+
!!find_option(arg)
|
359
|
+
end
|
360
|
+
|
361
|
+
# @see Formatter
|
362
|
+
# @return [String] Help string for this command.
|
363
|
+
def help
|
364
|
+
f = @config[:formatter]
|
365
|
+
|
366
|
+
f.header = @header.respond_to?(:call) ? @header.call : @header
|
367
|
+
f.footer = @footer.respond_to?(:call) ? @footer.call : @footer
|
368
|
+
f.commands = @commands if @commands
|
369
|
+
f.options = @options
|
370
|
+
|
371
|
+
f.to_s
|
372
|
+
end
|
373
|
+
|
374
|
+
private
|
375
|
+
|
376
|
+
# Sets a value in the state.
|
377
|
+
#
|
378
|
+
# @param state [#store, #[]]
|
379
|
+
# @param args [Array]
|
380
|
+
# @param scope [nil]
|
381
|
+
def set_state(state, args, scope=nil)
|
382
|
+
# scope will always be nil, so ignore it for Option compatibility
|
383
|
+
state[name].store :args, (@args.max <= 1 ? args[0] : args)
|
384
|
+
state
|
385
|
+
end
|
386
|
+
|
387
|
+
# Adds the '--help' option to the Command instance if it should be added.
|
388
|
+
def add_help_option
|
389
|
+
if @config[:help] && !(has_option?(:help) || has_option?(:h))
|
390
|
+
h = self # bind self so that it can be called in the block
|
391
|
+
self.option(:h, :help, "Display this help message", :tail => true) do
|
392
|
+
puts h.help
|
393
|
+
exit 0
|
490
394
|
end
|
491
395
|
end
|
492
396
|
end
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
h.bool "{prepend}{names.join(', ')} {spaces}{desc}"
|
504
|
-
h.flag "{prepend}{names.join(', ')} {args} {spaces}{desc} {options.bold}"
|
505
|
-
h.command "{prepend}{names.join(', ')} {spaces}{desc}"
|
506
|
-
end
|
507
|
-
}
|
508
|
-
|
397
|
+
|
398
|
+
# @return [String]
|
399
|
+
# Returns the last description to be set with {#description}, it then clears the
|
400
|
+
# stored description so that it is not returned twice.
|
401
|
+
def current_desc
|
402
|
+
r = @_last_desc
|
403
|
+
@_last_desc = ""
|
404
|
+
r
|
405
|
+
end
|
406
|
+
|
509
407
|
end
|
510
|
-
end
|
408
|
+
end
|