clamp 0.3.1 → 0.4.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/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
|