clamp 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +34 -16
- data/examples/admin +22 -0
- data/examples/gitdown +18 -9
- data/lib/clamp/attribute.rb +14 -7
- data/lib/clamp/command.rb +2 -0
- data/lib/clamp/option.rb +17 -0
- data/lib/clamp/option/parsing.rb +56 -7
- data/lib/clamp/parameter.rb +3 -0
- data/lib/clamp/parameter/parsing.rb +12 -0
- data/lib/clamp/version.rb +1 -1
- data/spec/clamp/command_spec.rb +190 -0
- data/spec/clamp/option_spec.rb +53 -6
- metadata +5 -4
data/README.markdown
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Clamp
|
2
2
|
=====
|
3
3
|
|
4
|
-
"Clamp" is a minimal framework for command-line utilities.
|
4
|
+
"Clamp" is a minimal framework for command-line utilities.
|
5
5
|
|
6
6
|
It handles boring stuff like parsing the command-line, and generating help, so you can get on with making your command actually do stuff.
|
7
7
|
|
@@ -42,7 +42,7 @@ Calling `run` on a command class creates an instance of it, then invokes it usin
|
|
42
42
|
|
43
43
|
SpeakCommand.run
|
44
44
|
|
45
|
-
Class-level methods like `option` and `parameter` declare attributes (in a similar way to `attr_accessor`), and arrange for them to be populated automatically based on command-line arguments. They are also used to generate `help` documentation.
|
45
|
+
Class-level methods like `option` and `parameter` declare attributes (in a similar way to `attr_accessor`), and arrange for them to be populated automatically based on command-line arguments. They are also used to generate `help` documentation.
|
46
46
|
|
47
47
|
Declaring options
|
48
48
|
-----------------
|
@@ -82,12 +82,20 @@ Some options are just boolean flags. Pass "`:flag`" as the second parameter to
|
|
82
82
|
|
83
83
|
For flag options, Clamp appends "`?`" to the generated reader method; ie. you get a method called "`#verbose?`", rather than just "`#verbose`".
|
84
84
|
|
85
|
-
Negatable flags are easy to generate, too:
|
85
|
+
Negatable flags are easy to generate, too:
|
86
86
|
|
87
87
|
option "--[no-]force", :flag, "be forceful (or not)"
|
88
88
|
|
89
89
|
Clamp will handle both "`--force`" and "`--no-force`" options, setting the value of "`#force?`" appropriately.
|
90
90
|
|
91
|
+
### Required options
|
92
|
+
|
93
|
+
Although 'required option' is a an oxymoron, Clamp lets you mark an option as required, and will verify that a value is provided:
|
94
|
+
|
95
|
+
option "--password", "PASSWORD", "the secret password", :required => true
|
96
|
+
|
97
|
+
Note that it makes no sense to mark a `:flag` option, or one with a `:default`, as `:required`.
|
98
|
+
|
91
99
|
Declaring parameters
|
92
100
|
--------------------
|
93
101
|
|
@@ -125,8 +133,8 @@ Clamp will verify that all required (ie. non-optional) parameters are present, a
|
|
125
133
|
|
126
134
|
### Validation block
|
127
135
|
|
128
|
-
Both `option` and `parameter` accept an optional block. If present, the block will be
|
129
|
-
called with the raw string option argument, and is expected to coerce it to
|
136
|
+
Both `option` and `parameter` accept an optional block. If present, the block will be
|
137
|
+
called with the raw string option argument, and is expected to coerce it to
|
130
138
|
the correct type, e.g.
|
131
139
|
|
132
140
|
option "--port", "PORT", "port to listen on" do |s|
|
@@ -143,19 +151,17 @@ If the block raises an ArgumentError, Clamp will catch it, and report that the v
|
|
143
151
|
While Clamp provides an attribute-writer method for each declared option or parameter, you always have the option of overriding it to provide custom argument-handling logic, e.g.
|
144
152
|
|
145
153
|
parameter "SERVER", "location of server"
|
146
|
-
|
154
|
+
|
147
155
|
def server=(server)
|
148
156
|
@server_address, @server_port = server.split(":")
|
149
157
|
end
|
150
158
|
|
151
159
|
### Default values
|
152
160
|
|
153
|
-
Default values can be specified for options:
|
161
|
+
Default values can be specified for options, and optional parameters:
|
154
162
|
|
155
163
|
option "--flavour", "FLAVOUR", "ice-cream flavour", :default => "chocolate"
|
156
164
|
|
157
|
-
and also for optional parameters
|
158
|
-
|
159
165
|
parameter "[HOST]", "server host", :default => "localhost"
|
160
166
|
|
161
167
|
For more advanced cases, you can also specify default values by defining a method called "`default_#{attribute_name}`":
|
@@ -168,6 +174,18 @@ For more advanced cases, you can also specify default values by defining a metho
|
|
168
174
|
http_port + 1
|
169
175
|
end
|
170
176
|
|
177
|
+
### Environment variable support
|
178
|
+
|
179
|
+
Options (and optional parameters) can also be associated with environment variables:
|
180
|
+
|
181
|
+
option "--port", "PORT", "the port to listen on", :environment_variable => "MYAPP_PORT" do |val|
|
182
|
+
val.to_i
|
183
|
+
end
|
184
|
+
|
185
|
+
parameter "[HOST]", "server address", :environment_variable => "MYAPP_HOST"
|
186
|
+
|
187
|
+
Clamp will check the specified envariables in the absence of values supplied on the command line, before looking for a default value.
|
188
|
+
|
171
189
|
Declaring Subcommands
|
172
190
|
---------------------
|
173
191
|
|
@@ -184,15 +202,15 @@ Unsuprisingly, subcommands are declared using the `subcommand` method. e.g.
|
|
184
202
|
end
|
185
203
|
|
186
204
|
end
|
187
|
-
|
205
|
+
|
188
206
|
end
|
189
207
|
|
190
208
|
Clamp generates an anonymous subclass of the current class, to represent the subcommand. Alternatively, you can provide an explicit subcommand class:
|
191
|
-
|
209
|
+
|
192
210
|
class MainCommand < Clamp::Command
|
193
211
|
|
194
212
|
subcommand "init", "Initialize the repository", InitCommand
|
195
|
-
|
213
|
+
|
196
214
|
end
|
197
215
|
|
198
216
|
class InitCommand < Clamp::Command
|
@@ -210,7 +228,7 @@ You can set a default subcommand, at the class level, as follows:
|
|
210
228
|
class MainCommand < Clamp::Command
|
211
229
|
|
212
230
|
self.default_subcommand = "status"
|
213
|
-
|
231
|
+
|
214
232
|
subcommand "status", "Display current status" do
|
215
233
|
|
216
234
|
def execute
|
@@ -218,7 +236,7 @@ You can set a default subcommand, at the class level, as follows:
|
|
218
236
|
end
|
219
237
|
|
220
238
|
end
|
221
|
-
|
239
|
+
|
222
240
|
end
|
223
241
|
|
224
242
|
Then, if when no SUBCOMMAND argument is provided, the default will be selected.
|
@@ -227,7 +245,7 @@ Then, if when no SUBCOMMAND argument is provided, the default will be selected.
|
|
227
245
|
|
228
246
|
Options are inheritable, so any options declared for a command are supported for it's sub-classes (e.g. those created using `subcommand`). Parameters, on the other hand, are not inherited - each subcommand must declare it's own parameter list.
|
229
247
|
|
230
|
-
Note that, if a subcommand accepts options, they must be specified on the command-line _after_ the subcommand name.
|
248
|
+
Note that, if a subcommand accepts options, they must be specified on the command-line _after_ the subcommand name.
|
231
249
|
|
232
250
|
Getting help
|
233
251
|
------------
|
@@ -271,4 +289,4 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
271
289
|
Contributing to Clamp
|
272
290
|
---------------------
|
273
291
|
|
274
|
-
Source-code for Clamp is [on Github](https://github.com/mdub/clamp).
|
292
|
+
Source-code for Clamp is [on Github](https://github.com/mdub/clamp).
|
data/examples/admin
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
# An example of subcommands
|
4
|
+
|
5
|
+
require "clamp"
|
6
|
+
|
7
|
+
class AdminCommand < Clamp::Command
|
8
|
+
|
9
|
+
option "--timeout", "SECONDS", "connection timeout", :default => 5, :environment_variable => "MYAPP_TIMEOUT" do |x|
|
10
|
+
Integer(x)
|
11
|
+
end
|
12
|
+
|
13
|
+
parameter "HOST", "server address"
|
14
|
+
parameter "[PORT]", "server port", :default => 80, :environment_variable => "MYAPP_PORT"
|
15
|
+
|
16
|
+
def execute
|
17
|
+
puts "trying to connect to #{host} on port #{port} (waiting up to #{timeout} seconds)"
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
AdminCommand.run
|
data/examples/gitdown
CHANGED
@@ -15,17 +15,22 @@ module GitDown
|
|
15
15
|
exit(0)
|
16
16
|
end
|
17
17
|
|
18
|
+
def say(message)
|
19
|
+
message = message.upcase if verbose?
|
20
|
+
puts message
|
21
|
+
end
|
22
|
+
|
18
23
|
end
|
19
24
|
|
20
25
|
class CloneCommand < AbstractCommand
|
21
|
-
|
26
|
+
|
22
27
|
parameter "REPOSITORY", "repository to clone"
|
23
28
|
parameter "[DIR]", "working directory", :default => "."
|
24
29
|
|
25
30
|
def execute
|
26
|
-
|
31
|
+
say "cloning to #{dir}"
|
27
32
|
end
|
28
|
-
|
33
|
+
|
29
34
|
end
|
30
35
|
|
31
36
|
class PullCommand < AbstractCommand
|
@@ -33,23 +38,27 @@ module GitDown
|
|
33
38
|
option "--[no-]commit", :flag, "Perform the merge and commit the result."
|
34
39
|
|
35
40
|
def execute
|
36
|
-
|
41
|
+
say "pulling"
|
37
42
|
end
|
38
|
-
|
43
|
+
|
39
44
|
end
|
40
45
|
|
41
46
|
class StatusCommand < AbstractCommand
|
42
|
-
|
47
|
+
|
43
48
|
option ["-s", "--short"], :flag, "Give the output in the short-format."
|
44
49
|
|
45
50
|
def execute
|
46
|
-
|
51
|
+
if short?
|
52
|
+
say "good"
|
53
|
+
else
|
54
|
+
say "it's all good ..."
|
55
|
+
end
|
47
56
|
end
|
48
|
-
|
57
|
+
|
49
58
|
end
|
50
59
|
|
51
60
|
class MainCommand < AbstractCommand
|
52
|
-
|
61
|
+
|
53
62
|
subcommand "clone", "Clone a remote repository.", CloneCommand
|
54
63
|
subcommand "pull", "Fetch and merge updates.", PullCommand
|
55
64
|
subcommand "status", "Display status of local repository.", StatusCommand
|
data/lib/clamp/attribute.rb
CHANGED
@@ -2,14 +2,10 @@ module Clamp
|
|
2
2
|
|
3
3
|
class Attribute
|
4
4
|
|
5
|
-
attr_reader :description, :attribute_name, :default_value
|
5
|
+
attr_reader :description, :attribute_name, :default_value, :environment_variable
|
6
6
|
|
7
7
|
def help_rhs
|
8
|
-
|
9
|
-
if defined?(@default_value)
|
10
|
-
rhs += " (default: #{@default_value.inspect})"
|
11
|
-
end
|
12
|
-
rhs
|
8
|
+
description + default_description
|
13
9
|
end
|
14
10
|
|
15
11
|
def help
|
@@ -32,6 +28,17 @@ module Clamp
|
|
32
28
|
"#{attribute_name}="
|
33
29
|
end
|
34
30
|
|
31
|
+
private
|
32
|
+
|
33
|
+
def default_description
|
34
|
+
default_sources = [
|
35
|
+
("$#{@environment_variable}" if defined?(@environment_variable)),
|
36
|
+
(@default_value.inspect if defined?(@default_value))
|
37
|
+
].compact
|
38
|
+
return "" if default_sources.empty?
|
39
|
+
" (default: " + default_sources.join(", or ") + ")"
|
40
|
+
end
|
41
|
+
|
35
42
|
end
|
36
43
|
|
37
|
-
end
|
44
|
+
end
|
data/lib/clamp/command.rb
CHANGED
data/lib/clamp/option.rb
CHANGED
@@ -14,6 +14,19 @@ module Clamp
|
|
14
14
|
if options.has_key?(:default)
|
15
15
|
@default_value = options[:default]
|
16
16
|
end
|
17
|
+
if options.has_key?(:environment_variable)
|
18
|
+
@environment_variable = options[:environment_variable]
|
19
|
+
end
|
20
|
+
if options.has_key?(:required)
|
21
|
+
@required = options[:required]
|
22
|
+
# Do some light validation for conflicting settings.
|
23
|
+
if options.has_key?(:default)
|
24
|
+
raise ArgumentError, "Specifying a :default value also :required doesn't make sense"
|
25
|
+
end
|
26
|
+
if type == :flag
|
27
|
+
raise ArgumentError, "A required flag (boolean) doesn't make sense."
|
28
|
+
end
|
29
|
+
end
|
17
30
|
end
|
18
31
|
|
19
32
|
attr_reader :switches, :type
|
@@ -30,6 +43,10 @@ module Clamp
|
|
30
43
|
recognised_switches.member?(switch)
|
31
44
|
end
|
32
45
|
|
46
|
+
def required?
|
47
|
+
@required
|
48
|
+
end
|
49
|
+
|
33
50
|
def flag?
|
34
51
|
@type == :flag
|
35
52
|
end
|
data/lib/clamp/option/parsing.rb
CHANGED
@@ -3,23 +3,45 @@ module Clamp
|
|
3
3
|
|
4
4
|
module Parsing
|
5
5
|
|
6
|
+
# For :flag options with environment variables attached, this is a list
|
7
|
+
# of possible values that are accepted as 'true'
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
#
|
11
|
+
# option "--foo", :flag, "Use foo", :environment_variable => "FOO"
|
12
|
+
#
|
13
|
+
# All of these will set 'foo' to true:
|
14
|
+
#
|
15
|
+
# FOO=1 ./myprogram
|
16
|
+
# FOO=true ./myprogram
|
17
|
+
# FOO=yes ./myprogram
|
18
|
+
# FOO=on ./myprogram
|
19
|
+
# FOO=enable ./myprogram
|
20
|
+
#
|
21
|
+
# See {Clamp::Command.option} for more information.
|
22
|
+
TRUTHY_ENVIRONMENT_VALUES = %w(1 yes enable on true)
|
23
|
+
|
6
24
|
protected
|
7
25
|
|
8
26
|
def parse_options
|
9
|
-
while remaining_arguments.first =~
|
27
|
+
while remaining_arguments.first =~ /\A-/
|
10
28
|
|
11
29
|
switch = remaining_arguments.shift
|
12
30
|
break if switch == "--"
|
13
31
|
|
14
32
|
case switch
|
15
|
-
when
|
33
|
+
when /\A(-\w)(.+)\z/m # combined short options
|
16
34
|
switch = $1
|
17
|
-
|
18
|
-
|
35
|
+
if find_option(switch).flag?
|
36
|
+
remaining_arguments.unshift("-" + $2)
|
37
|
+
else
|
38
|
+
remaining_arguments.unshift($2)
|
39
|
+
end
|
40
|
+
when /\A(--[^=]+)=(.*)\z/m
|
19
41
|
switch = $1
|
20
42
|
remaining_arguments.unshift($2)
|
21
43
|
end
|
22
|
-
|
44
|
+
|
23
45
|
option = find_option(switch)
|
24
46
|
value = option.extract_value(switch, remaining_arguments)
|
25
47
|
|
@@ -30,16 +52,43 @@ module Clamp
|
|
30
52
|
end
|
31
53
|
|
32
54
|
end
|
55
|
+
|
56
|
+
# Verify that all required options are present
|
57
|
+
self.class.recognised_options.each do |option|
|
58
|
+
# If this option is required and the value is nil, there's an error.
|
59
|
+
if option.required? and send(option.attribute_name).nil?
|
60
|
+
message = "option '#{option.switches.first}'"
|
61
|
+
if option.environment_variable
|
62
|
+
message += " (or env #{option.environment_variable})"
|
63
|
+
end
|
64
|
+
message += " is required"
|
65
|
+
signal_usage_error message
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def parse_environment_options
|
71
|
+
self.class.recognised_options.each do |option|
|
72
|
+
next if option.environment_variable.nil?
|
73
|
+
next unless ENV.has_key?(option.environment_variable)
|
74
|
+
value = ENV[option.environment_variable]
|
75
|
+
if option.flag?
|
76
|
+
# Set true if the environment value is truthy.
|
77
|
+
send("#{option.attribute_name}=", TRUTHY_ENVIRONMENT_VALUES.include?(value))
|
78
|
+
else
|
79
|
+
send("#{option.attribute_name}=", value)
|
80
|
+
end
|
81
|
+
end
|
33
82
|
end
|
34
83
|
|
35
84
|
private
|
36
85
|
|
37
86
|
def find_option(switch)
|
38
|
-
self.class.find_option(switch) ||
|
87
|
+
self.class.find_option(switch) ||
|
39
88
|
signal_usage_error("Unrecognised option '#{switch}'")
|
40
89
|
end
|
41
90
|
|
42
91
|
end
|
43
92
|
|
44
93
|
end
|
45
|
-
end
|
94
|
+
end
|
data/lib/clamp/parameter.rb
CHANGED
@@ -18,6 +18,18 @@ module Clamp
|
|
18
18
|
|
19
19
|
end
|
20
20
|
|
21
|
+
def parse_environment_parameters
|
22
|
+
|
23
|
+
self.class.parameters.each do |parameter|
|
24
|
+
next if parameter.environment_variable.nil?
|
25
|
+
next unless ENV.has_key?(parameter.environment_variable)
|
26
|
+
# Set the parameter value if it's environment variable is present
|
27
|
+
value = ENV[parameter.environment_variable]
|
28
|
+
send("#{parameter.attribute_name}=", value)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
21
33
|
end
|
22
34
|
|
23
35
|
end
|
data/lib/clamp/version.rb
CHANGED
data/spec/clamp/command_spec.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
|
1
2
|
require 'spec_helper'
|
2
3
|
|
3
4
|
describe Clamp::Command do
|
@@ -110,6 +111,124 @@ describe Clamp::Command do
|
|
110
111
|
|
111
112
|
end
|
112
113
|
|
114
|
+
describe "with :environment_variable value" do
|
115
|
+
|
116
|
+
before do
|
117
|
+
@command.class.option "--port", "PORT", "port to listen on", :default => 4321, :environment_variable => "PORT" do |value|
|
118
|
+
value.to_i
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should use the default if neither flag nor env var are present" do
|
123
|
+
ENV.delete("PORT")
|
124
|
+
@command.parse([])
|
125
|
+
@command.port.should == 4321
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should use the env value if present (instead of default)" do
|
129
|
+
ENV["PORT"] = rand(10000).to_s
|
130
|
+
@command.parse([])
|
131
|
+
@command.port.should == ENV["PORT"].to_i
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should use the the flag value if present (instead of env)" do
|
135
|
+
ENV["PORT"] = "12345"
|
136
|
+
@command.parse(%w(--port 1500))
|
137
|
+
@command.port.should == 1500
|
138
|
+
end
|
139
|
+
|
140
|
+
describe "#help" do
|
141
|
+
|
142
|
+
it "describes the default value and env usage" do
|
143
|
+
@command.help.should include("port to listen on (default: $PORT, or 4321)")
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
describe "with :environment_variable value on a :flag option" do
|
151
|
+
|
152
|
+
before do
|
153
|
+
@command.class.option "--[no-]enable", :flag, "enable?", :default => false, :environment_variable => "ENABLE"
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should use the default if neither flag nor env var are present" do
|
157
|
+
@command.parse([])
|
158
|
+
@command.enable?.should == false
|
159
|
+
end
|
160
|
+
|
161
|
+
Clamp::Option::Parsing::TRUTHY_ENVIRONMENT_VALUES.each do |value|
|
162
|
+
it "should use environment value '#{value}' to mean true" do
|
163
|
+
ENV["ENABLE"] = value
|
164
|
+
@command.parse([])
|
165
|
+
@command.enable?.should == true
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# Make sure tests fail if ever the TRUTHY_ENVIRONMENT_VALUES loses a
|
170
|
+
# value. This is just a safety check to make sure maintainers update
|
171
|
+
# any relevant docs and aware that they could be breaking compatibility.
|
172
|
+
it "should accept only these values as 'truthy' environment values: 1, yes, enable, on, true" do
|
173
|
+
Clamp::Option::Parsing::TRUTHY_ENVIRONMENT_VALUES.should == %w(1 yes enable on true)
|
174
|
+
end
|
175
|
+
|
176
|
+
it "should use an env value other than truthy ones to mean false" do
|
177
|
+
[nil, "0", "no", "whatever"].each do |val|
|
178
|
+
ENV["ENABLE"] = val
|
179
|
+
@command.parse([])
|
180
|
+
@command.enable?.should == false
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
it "should use the the flag value if present (instead of env)" do
|
185
|
+
ENV["ENABLE"] = "1"
|
186
|
+
@command.parse(%w(--no-enable))
|
187
|
+
@command.enable?.should == false
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
191
|
+
|
192
|
+
describe "with :required value" do
|
193
|
+
|
194
|
+
before do
|
195
|
+
@command.class.option "--port", "PORT", "port to listen on", :required => true
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should fail if a required option is not provided" do
|
199
|
+
expect { @command.parse([]) }.to raise_error(Clamp::UsageError)
|
200
|
+
end
|
201
|
+
|
202
|
+
it "should succeed if a required option is provided" do
|
203
|
+
@command.parse(["--port", "12345"])
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
207
|
+
|
208
|
+
describe "with :required value with :env" do
|
209
|
+
|
210
|
+
before do
|
211
|
+
@command.class.option "--port", "PORT", "port to listen on", :required => true, :environment_variable => "PORT"
|
212
|
+
end
|
213
|
+
|
214
|
+
it "should fail if a required option is not provided" do
|
215
|
+
ENV.delete("PORT")
|
216
|
+
expect { @command.parse([]) }.to raise_error(Clamp::UsageError)
|
217
|
+
end
|
218
|
+
|
219
|
+
it "should succeed if a required option is provided via arguments" do
|
220
|
+
ENV.delete("PORT")
|
221
|
+
@command.parse(["--port", "12345"])
|
222
|
+
end
|
223
|
+
|
224
|
+
it "should succeed if a required option is provided via env" do
|
225
|
+
ENV["PORT"] = "12345"
|
226
|
+
@command.parse([])
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|
230
|
+
|
231
|
+
|
113
232
|
describe "with a block" do
|
114
233
|
|
115
234
|
before do
|
@@ -178,6 +297,18 @@ describe Clamp::Command do
|
|
178
297
|
|
179
298
|
end
|
180
299
|
|
300
|
+
describe "with a value appended to a short option" do
|
301
|
+
|
302
|
+
before do
|
303
|
+
@command.parse(%w(-fstrawberry))
|
304
|
+
end
|
305
|
+
|
306
|
+
it "works as though the value were separated" do
|
307
|
+
@command.flavour.should == "strawberry"
|
308
|
+
end
|
309
|
+
|
310
|
+
end
|
311
|
+
|
181
312
|
describe "with combined short options" do
|
182
313
|
|
183
314
|
before do
|
@@ -213,6 +344,20 @@ describe Clamp::Command do
|
|
213
344
|
|
214
345
|
end
|
215
346
|
|
347
|
+
describe "with multi-line arguments that look like options" do
|
348
|
+
|
349
|
+
before do
|
350
|
+
@command.parse(["foo\n--flavour=strawberry", "bar\n-cblue"])
|
351
|
+
end
|
352
|
+
|
353
|
+
it "treats them as positional arguments" do
|
354
|
+
@command.arguments.should == ["foo\n--flavour=strawberry", "bar\n-cblue"]
|
355
|
+
@command.flavour.should be_nil
|
356
|
+
@command.color.should be_nil
|
357
|
+
end
|
358
|
+
|
359
|
+
end
|
360
|
+
|
216
361
|
describe "with an option terminator" do
|
217
362
|
|
218
363
|
it "considers everything after the terminator to be an argument" do
|
@@ -438,6 +583,40 @@ describe Clamp::Command do
|
|
438
583
|
|
439
584
|
end
|
440
585
|
|
586
|
+
describe "with :environment_variable value" do
|
587
|
+
|
588
|
+
before do
|
589
|
+
@command.class.parameter "[FILE]", "a file", :environment_variable => "FILE",
|
590
|
+
:default => "/dev/null"
|
591
|
+
end
|
592
|
+
|
593
|
+
it "should use the default if neither flag nor env var are present" do
|
594
|
+
@command.parse([])
|
595
|
+
@command.file.should == "/dev/null"
|
596
|
+
end
|
597
|
+
|
598
|
+
it "should use the env value if present (instead of default)" do
|
599
|
+
ENV["FILE"] = "/etc/motd"
|
600
|
+
@command.parse([])
|
601
|
+
@command.file.should == ENV["FILE"]
|
602
|
+
end
|
603
|
+
|
604
|
+
it "should use the the flag value if present (instead of env)" do
|
605
|
+
ENV["FILE"] = "/etc/motd"
|
606
|
+
@command.parse(%w(/bin/sh))
|
607
|
+
@command.file.should == "/bin/sh"
|
608
|
+
end
|
609
|
+
|
610
|
+
describe "#help" do
|
611
|
+
|
612
|
+
it "describes the default value and env usage" do
|
613
|
+
@command.help.should include(%{ (default: $FILE, or "/dev/null")})
|
614
|
+
end
|
615
|
+
|
616
|
+
end
|
617
|
+
|
618
|
+
end
|
619
|
+
|
441
620
|
end
|
442
621
|
|
443
622
|
describe "with no parameters declared" do
|
@@ -503,6 +682,17 @@ describe Clamp::Command do
|
|
503
682
|
|
504
683
|
end
|
505
684
|
|
685
|
+
describe "with multi-line arguments" do
|
686
|
+
|
687
|
+
it "parses them correctly" do
|
688
|
+
@command.parse(["foo\nhi", "bar", "baz"])
|
689
|
+
@command.x.should == "foo\nhi"
|
690
|
+
@command.y.should == "bar"
|
691
|
+
@command.z.should == "baz"
|
692
|
+
end
|
693
|
+
|
694
|
+
end
|
695
|
+
|
506
696
|
describe "with too many arguments" do
|
507
697
|
|
508
698
|
it "raises a UsageError" do
|
data/spec/clamp/option_spec.rb
CHANGED
@@ -57,7 +57,7 @@ describe Clamp::Option do
|
|
57
57
|
end
|
58
58
|
|
59
59
|
describe "flag" do
|
60
|
-
|
60
|
+
|
61
61
|
before do
|
62
62
|
@option = Clamp::Option.new("--verbose", :flag, "Blah blah blah")
|
63
63
|
end
|
@@ -69,11 +69,11 @@ describe Clamp::Option do
|
|
69
69
|
end
|
70
70
|
|
71
71
|
end
|
72
|
-
|
72
|
+
|
73
73
|
end
|
74
74
|
|
75
75
|
describe "negatable flag" do
|
76
|
-
|
76
|
+
|
77
77
|
before do
|
78
78
|
@option = Clamp::Option.new("--[no-]force", :flag, "Force installation")
|
79
79
|
end
|
@@ -91,7 +91,7 @@ describe Clamp::Option do
|
|
91
91
|
end
|
92
92
|
|
93
93
|
end
|
94
|
-
|
94
|
+
|
95
95
|
describe "#attribute_name" do
|
96
96
|
|
97
97
|
it "is derived from the (long) switch" do
|
@@ -99,7 +99,7 @@ describe Clamp::Option do
|
|
99
99
|
end
|
100
100
|
|
101
101
|
end
|
102
|
-
|
102
|
+
|
103
103
|
end
|
104
104
|
|
105
105
|
describe "with both short and long switches" do
|
@@ -123,7 +123,40 @@ describe Clamp::Option do
|
|
123
123
|
|
124
124
|
end
|
125
125
|
|
126
|
+
describe "with an associated environment variable" do
|
127
|
+
|
128
|
+
before do
|
129
|
+
@option = Clamp::Option.new("-x", "X", "mystery option", :environment_variable => "APP_X")
|
130
|
+
end
|
131
|
+
|
132
|
+
describe "#help" do
|
133
|
+
|
134
|
+
it "describes environment variable" do
|
135
|
+
@option.help.should == ["-x X", "mystery option (default: $APP_X)"]
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
describe "and a default value" do
|
141
|
+
|
142
|
+
before do
|
143
|
+
@option = Clamp::Option.new("-x", "X", "mystery option", :environment_variable => "APP_X", :default => "xyz")
|
144
|
+
end
|
145
|
+
|
146
|
+
describe "#help" do
|
147
|
+
|
148
|
+
it "describes both environment variable and default" do
|
149
|
+
@option.help.should == ["-x X", %{mystery option (default: $APP_X, or "xyz")}]
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
|
126
158
|
describe "in subcommand" do
|
159
|
+
|
127
160
|
before do
|
128
161
|
|
129
162
|
@command = Class.new(Clamp::Command) do
|
@@ -145,5 +178,19 @@ describe Clamp::Option do
|
|
145
178
|
end
|
146
179
|
|
147
180
|
end
|
148
|
-
|
181
|
+
|
182
|
+
describe "a required option" do
|
183
|
+
it "rejects :default" do
|
184
|
+
expect do
|
185
|
+
Clamp::Option.new("--key-file", "FILE", "SSH identity",
|
186
|
+
:required => true, :default => "hello")
|
187
|
+
end.to raise_error(ArgumentError)
|
188
|
+
end
|
189
|
+
|
190
|
+
it "rejects :flag options" do
|
191
|
+
expect do
|
192
|
+
Clamp::Option.new("--awesome", :flag, "Be awesome?", :required => true)
|
193
|
+
end.to raise_error(ArgumentError)
|
194
|
+
end
|
195
|
+
end
|
149
196
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: clamp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-05-13 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: ! "Clamp provides an object-model for command-line utilities. \nIt handles
|
15
15
|
parsing of command-line options, and generation of usage help.\n"
|
@@ -24,6 +24,7 @@ files:
|
|
24
24
|
- README.markdown
|
25
25
|
- Rakefile
|
26
26
|
- clamp.gemspec
|
27
|
+
- examples/admin
|
27
28
|
- examples/flipflop
|
28
29
|
- examples/fubar
|
29
30
|
- examples/gitdown
|
@@ -64,7 +65,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
64
65
|
version: '0'
|
65
66
|
segments:
|
66
67
|
- 0
|
67
|
-
hash: -
|
68
|
+
hash: -1903818616036317857
|
68
69
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
70
|
none: false
|
70
71
|
requirements:
|
@@ -73,7 +74,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
73
74
|
version: '0'
|
74
75
|
segments:
|
75
76
|
- 0
|
76
|
-
hash: -
|
77
|
+
hash: -1903818616036317857
|
77
78
|
requirements: []
|
78
79
|
rubyforge_project:
|
79
80
|
rubygems_version: 1.8.21
|