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 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
- raise NotImplementedError
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
- raise NotImplementedError
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
- raise NotImplementedError
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
@@ -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
- rhs = description
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
@@ -46,7 +46,9 @@ module Clamp
46
46
  #
47
47
  def parse(arguments)
48
48
  @remaining_arguments = arguments.dup
49
+ parse_environment_options
49
50
  parse_options
51
+ parse_environment_parameters
50
52
  parse_parameters
51
53
  parse_subcommand
52
54
  handle_remaining_arguments
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
@@ -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 /^(-\w)(.+)$/ # combined short options
33
+ when /\A(-\w)(.+)\z/m # combined short options
16
34
  switch = $1
17
- remaining_arguments.unshift("-#{$2}")
18
- when /^(--[^=]+)=(.*)/
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
@@ -14,6 +14,9 @@ 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
17
20
  end
18
21
 
19
22
  attr_reader :name, :attribute_name
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Clamp
2
- VERSION = "0.3.1".freeze
2
+ VERSION = "0.4.0".freeze
3
3
  end
@@ -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
@@ -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.3.1
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-04-03 00:00:00.000000000 Z
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: -1856448867096069066
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: -1856448867096069066
77
+ hash: -1903818616036317857
77
78
  requirements: []
78
79
  rubyforge_project:
79
80
  rubygems_version: 1.8.21