jls-clamp 0.3.1 → 0.3.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -88,6 +88,32 @@ Negatable flags are easy to generate, too:
88
88
 
89
89
  Clamp will handle both "`--force`" and "`--no-force`" options, setting the value of "`#force?`" appropriately.
90
90
 
91
+ ### Values from the environment
92
+
93
+ Sometimes you'll want to pass in option values from the environment:
94
+
95
+ option "--port", "PORT", "the port to listen on", :env => "PORT" do |val|
96
+ val.to_i
97
+ end
98
+
99
+ The above means that in the absence of '--port' on the command line, Clamp will
100
+ check for `PORT` in the environment. This lets you do:
101
+
102
+ % export PORT=8080
103
+ % ./mycommand
104
+
105
+ ### Required options
106
+
107
+ While a 'required option' is a bit of an oxymoron, it is common to use options
108
+ as named parameters to your program. Clamp lets you make an option required:
109
+
110
+ option "--password", "PASSWORD", "the secret password", :required => true
111
+
112
+ Special notes about required options:
113
+
114
+ * You may not use :required and :default values in the same option ('require' doesn't make sense if you provide a default)
115
+ * You may not use :required on :flag options. Since :flag options are boolean, a 'required flag' would mean "always true" ;)
116
+
91
117
  Declaring parameters
92
118
  --------------------
93
119
 
@@ -116,6 +142,18 @@ Three dots at the end of a parameter name makes it "greedy" - it will consume al
116
142
 
117
143
  The suffix "`_list`" is appended to the default attribute name for greedy parameters; in this case, an attribute called "`file_list`" would be generated.
118
144
 
145
+ ### Parameters from the environment
146
+
147
+ Optional parameters can have values given from the environment the same way options can.
148
+
149
+ parameter "[EXAMPLE]", "This is an example", :env => "EXAMPLE"
150
+
151
+ The above means that in the absence of a value on the command line, Clamp will
152
+ check for `EXAMPLE` in the environment. This lets you do:
153
+
154
+ % export EXAMPLE="hello"
155
+ % ./mycommand
156
+
119
157
  Parsing and validation of options and parameters
120
158
  ------------------------------------------------
121
159
 
@@ -2,15 +2,15 @@ module Clamp
2
2
 
3
3
  class Attribute
4
4
 
5
- attr_reader :description, :attribute_name, :default_value, :env_var
5
+ attr_reader :description, :attribute_name, :default_value, :environment_variable
6
6
 
7
7
  def help_rhs
8
8
  rhs = description
9
9
  if defined?(@default_value)
10
10
  rhs += " (default: #{@default_value.inspect})"
11
11
  end
12
- if defined?(@env_var)
13
- rhs += " (env: #{@env_var.inspect})"
12
+ if defined?(@environment_variable)
13
+ rhs += " (env: #{@environment_variable.inspect})"
14
14
  end
15
15
  rhs
16
16
  end
@@ -46,8 +46,9 @@ module Clamp
46
46
  #
47
47
  def parse(arguments)
48
48
  @remaining_arguments = arguments.dup
49
- parse_environment
49
+ parse_environment_options
50
50
  parse_options
51
+ parse_environment_parameters
51
52
  parse_parameters
52
53
  parse_subcommand
53
54
  handle_remaining_arguments
@@ -15,7 +15,17 @@ module Clamp
15
15
  @default_value = options[:default]
16
16
  end
17
17
  if options.has_key?(:env)
18
- @env_var = options[:env]
18
+ @environment_variable = options[:env]
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
19
29
  end
20
30
  end
21
31
 
@@ -33,6 +43,10 @@ module Clamp
33
43
  recognised_switches.member?(switch)
34
44
  end
35
45
 
46
+ def required?
47
+ @required
48
+ end
49
+
36
50
  def flag?
37
51
  @type == :flag
38
52
  end
@@ -3,6 +3,24 @@ 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", :env => "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
@@ -30,16 +48,29 @@ module Clamp
30
48
  end
31
49
 
32
50
  end
51
+
52
+ # Verify that all required options are present
53
+ self.class.recognised_options.each do |option|
54
+ # If this option is required and the value is nil, there's an error.
55
+ if option.required? and send(option.attribute_name).nil?
56
+ message = "option '#{option.switches.first}'"
57
+ if option.environment_variable
58
+ message += " (or env #{option.environment_variable})"
59
+ end
60
+ message += " is required"
61
+ signal_usage_error message
62
+ end
63
+ end
33
64
  end
34
65
 
35
- def parse_environment
66
+ def parse_environment_options
36
67
  self.class.recognised_options.each do |option|
37
- next if option.env_var.nil?
38
- next unless ENV.has_key?(option.env_var)
39
- value = ENV[option.env_var]
68
+ next if option.environment_variable.nil?
69
+ next unless ENV.has_key?(option.environment_variable)
70
+ value = ENV[option.environment_variable]
40
71
  if option.flag?
41
- # Set true if the env var is "1" false otherwise.
42
- send("#{option.attribute_name}=", value == "1")
72
+ # Set true if the environment value is truthy.
73
+ send("#{option.attribute_name}=", TRUTHY_ENVIRONMENT_VALUES.include?(value))
43
74
  else
44
75
  send("#{option.attribute_name}=", value)
45
76
  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?(:env)
18
+ @environment_variable = options[:env]
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
@@ -1,3 +1,3 @@
1
1
  module Clamp
2
- VERSION = "0.3.1".freeze
2
+ VERSION = "0.3.1.2".freeze
3
3
  end
@@ -119,6 +119,7 @@ describe Clamp::Command do
119
119
  end
120
120
 
121
121
  it "should use the default if neither flag nor env var are present" do
122
+ ENV.delete("PORT")
122
123
  @command.parse([])
123
124
  @command.port.should == 4321
124
125
  end
@@ -156,16 +157,27 @@ describe Clamp::Command do
156
157
  @command.enable?.should == false
157
158
  end
158
159
 
159
- it "should use an env value of '1' to mean truth" do
160
- ENV["ENABLE"] = "1"
161
- @command.parse([])
162
- @command.enable?.should == true
160
+ Clamp::Option::Parsing::TRUTHY_ENVIRONMENT_VALUES.each do |value|
161
+ it "should use environment value '#{value}' to mean true" do
162
+ ENV["ENABLE"] = value
163
+ @command.parse([])
164
+ @command.enable?.should == true
165
+ end
163
166
  end
164
167
 
165
- it "should use an env value other than '1' to mean false" do
166
- ENV["ENABLE"] = "0"
167
- @command.parse([])
168
- @command.enable?.should == false
168
+ # Make sure tests fail if ever the TRUTHY_ENVIRONMENT_VALUES loses a
169
+ # value. This is just a safety check to make sure maintainers update
170
+ # any relevant docs and aware that they could be breaking compatibility.
171
+ it "should accept only these values as 'truthy' environment values: 1, yes, enable, on, true" do
172
+ Clamp::Option::Parsing::TRUTHY_ENVIRONMENT_VALUES.should == %w(1 yes enable on true)
173
+ end
174
+
175
+ it "should use an env value other than truthy ones to mean false" do
176
+ [nil, "0", "no", "whatever"].each do |val|
177
+ ENV["ENABLE"] = val
178
+ @command.parse([])
179
+ @command.enable?.should == false
180
+ end
169
181
  end
170
182
 
171
183
  it "should use the the flag value if present (instead of env)" do
@@ -184,6 +196,46 @@ describe Clamp::Command do
184
196
 
185
197
  end
186
198
 
199
+ describe "with :required value" do
200
+
201
+ before do
202
+ @command.class.option "--port", "PORT", "port to listen on", :required => true
203
+ end
204
+
205
+ it "should fail if a required option is not provided" do
206
+ expect { @command.parse([]) }.to raise_error(Clamp::UsageError)
207
+ end
208
+
209
+ it "should succeed if a required option is provided" do
210
+ @command.parse(["--port", "12345"])
211
+ end
212
+
213
+ end
214
+
215
+ describe "with :required value with :env" do
216
+
217
+ before do
218
+ @command.class.option "--port", "PORT", "port to listen on", :required => true, :env => "PORT"
219
+ end
220
+
221
+ it "should fail if a required option is not provided" do
222
+ ENV.delete("PORT")
223
+ expect { @command.parse([]) }.to raise_error(Clamp::UsageError)
224
+ end
225
+
226
+ it "should succeed if a required option is provided via arguments" do
227
+ ENV.delete("PORT")
228
+ @command.parse(["--port", "12345"])
229
+ end
230
+
231
+ it "should succeed if a required option is provided via env" do
232
+ ENV["PORT"] = "12345"
233
+ @command.parse([])
234
+ end
235
+
236
+ end
237
+
238
+
187
239
  describe "with a block" do
188
240
 
189
241
  before do
@@ -512,6 +564,40 @@ describe Clamp::Command do
512
564
 
513
565
  end
514
566
 
567
+ describe "with :env value" do
568
+
569
+ before do
570
+ @command.class.parameter "[FILE]", "a file", :env => "FILE",
571
+ :default => "default"
572
+ end
573
+
574
+ it "should use the default if neither flag nor env var are present" do
575
+ @command.parse([])
576
+ @command.file.should == "default"
577
+ end
578
+
579
+ it "should use the env value if present (instead of default)" do
580
+ ENV["FILE"] = "/etc/motd"
581
+ @command.parse([])
582
+ @command.file.should == ENV["FILE"]
583
+ end
584
+
585
+ it "should use the the flag value if present (instead of env)" do
586
+ ENV["FILE"] = "/etc/motd"
587
+ @command.parse(%w(/bin/sh))
588
+ @command.file.should == "/bin/sh"
589
+ end
590
+
591
+ describe "#help" do
592
+
593
+ it "describes the default value and env usage" do
594
+ @command.help.should include("a file (default: \"default\") (env: \"FILE\")")
595
+ end
596
+
597
+ end
598
+
599
+ end
600
+
515
601
  end
516
602
 
517
603
  describe "with no parameters declared" do
@@ -145,5 +145,20 @@ describe Clamp::Option do
145
145
  end
146
146
 
147
147
  end
148
-
148
+
149
+ describe "a required option" do
150
+ it "rejects :default" do
151
+ expect do
152
+ Clamp::Option.new("--key-file", "FILE", "SSH identity",
153
+ :required => true, :default => "hello")
154
+ end.to raise_error(ArgumentError)
155
+ end
156
+
157
+ it "rejects :flag options" do
158
+ expect do
159
+ Clamp::Option.new("--awesome", :flag, "Be awesome?", :required => true)
160
+ end.to raise_error(ArgumentError)
161
+ end
162
+ end
163
+
149
164
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jls-clamp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.1.2
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-25 00:00:00.000000000 Z
12
+ date: 2012-04-30 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"