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