jls-clamp 0.3.1 → 0.3.1.2

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.
@@ -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"