flags 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.markdown +41 -0
- data/lib/flags.rb +545 -0
- data/test/flags_test.rb +277 -0
- metadata +70 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright © 2011 Ooyala, Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
Flags - A Ruby Library for Simple Command-Line Flags
|
2
|
+
====================================================
|
3
|
+
Flags is a framework for Ruby which allows the definition of command-line flags, which are parsed in and can be accessed smartly from within your Ruby code. This framework allows for numerous flag types and takes care of the process of type conversion and flag validation (type and value checking).
|
4
|
+
|
5
|
+
This flags framework is modeled after, and loosely resembles Google's, python-gflags library. The advantage of these kinds of flags over other well-known libraries is the ability to define flags in the place at which they are used (libraries, utility files, setup files), rather than entirely in the main execution function of the calling program.
|
6
|
+
|
7
|
+
Usage
|
8
|
+
-----
|
9
|
+
Require the "flags" gem at the top of your file. Then, define flags after require statements but before any other code. A flag with a given name may not be defined more than once, and doing so will raise an error. Examples:
|
10
|
+
|
11
|
+
Flags.define_string(:my_string_flag, "default_value", "This is my comment for a string flag")
|
12
|
+
Flags.define_symbol(:my_symbol_flag, :default_val, "This is my comment for a symbol flag")
|
13
|
+
Flags.define_int(:my_int_flag, 1000, "This is my comment for an int flag")
|
14
|
+
Flags.define_float(:my_float_flag, 2.0, "This is my comment for a float flag")
|
15
|
+
Flags.define_bool(:my_bool_flag, true, "This is my comment for a bool flag")
|
16
|
+
|
17
|
+
In your main:
|
18
|
+
|
19
|
+
if __FILE__ == $0
|
20
|
+
Flags.init # This will parse and consume all the flags from your command line that match a defined flag
|
21
|
+
end
|
22
|
+
|
23
|
+
Run your file with some command-line flags:
|
24
|
+
./myfile -my_string_flag "foo" -my_int_flag 2
|
25
|
+
|
26
|
+
Note that specifying a boolean flag requires the word "true" or "false" following the flag name.
|
27
|
+
|
28
|
+
Then your code can access the flags any time after Flags.init with:
|
29
|
+
Flags.my_string_flag and Flags.my_int_flag
|
30
|
+
|
31
|
+
Contributing
|
32
|
+
------------
|
33
|
+
Feel free to create tickets for enhancement ideas, or just fork and submit a pull request on our [GitHub page](https://github.com/ooyala/flags). Note that this first distribution is pre-release code, and while it is stable, there is potential for significant changes in future releases.
|
34
|
+
|
35
|
+
License
|
36
|
+
-------
|
37
|
+
Licensed under the [MIT license](http://opensource.org/licenses/mit-license.php).
|
38
|
+
|
39
|
+
Credits
|
40
|
+
-------
|
41
|
+
Copyright © 2011 Ooyala, Inc.
|
data/lib/flags.rb
ADDED
@@ -0,0 +1,545 @@
|
|
1
|
+
# Copyright © 2011 Ooyala, Inc.
|
2
|
+
# A simple command-line flag framework.
|
3
|
+
#
|
4
|
+
# Usage:
|
5
|
+
#
|
6
|
+
# Define flags at the top of your ruby file, after require statements but before any other code. A flag with
|
7
|
+
# a given name may not be defined more than once, and doing so will raise an error. Examples:
|
8
|
+
#
|
9
|
+
# Flags.define_string(:my_string_flag, "default_value", "This is my comment for a string flag")
|
10
|
+
# Flags.define_symbol(:my_symbol_flag, :default_val, "This is my comment for a symbol flag")
|
11
|
+
# Flags.define_int(:my_int_flag, 1000, "This is my comment for an int flag")
|
12
|
+
# Flags.define_float(:my_float_flag, 2.0, "This is my comment for a float flag")
|
13
|
+
# Flags.define_bool(:my_bool_flag, true, "This is my comment for a bool flag")
|
14
|
+
#
|
15
|
+
# In your main:
|
16
|
+
# if __FILE__ == $0
|
17
|
+
# Flags.init # This will parse and consume all the flags from your command line that match a defined flag
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# Run your file with some command-line flags:
|
21
|
+
# ./myfile -my_string_flag "foo" -my_int_flag 2
|
22
|
+
#
|
23
|
+
# Note that specifying a boolean flag requires the word "true" or "false" following the flag name.
|
24
|
+
#
|
25
|
+
# Then your code can access the flags any time after Flags.init with
|
26
|
+
# Flags.my_string_flag and Flags.my_int_flag
|
27
|
+
|
28
|
+
require 'yaml'
|
29
|
+
|
30
|
+
# Note that the Flags class is a singleton, and all of its methods and data storage are class-level rather
|
31
|
+
# than instance-level. It is currently not possible to have multiple Flags instances within a single process.
|
32
|
+
class Flags
|
33
|
+
# Parses the command line args and extracts flag value pairs. Should be called at least once before getting
|
34
|
+
# or setting any flag value. In the future, Flags will start throwing errors if any flag is accessed
|
35
|
+
# prior to Flags.init being called.
|
36
|
+
#
|
37
|
+
# NOTE: this method HAS SIDE EFFECTS - it removes the flag name/value pairs from the supplied array of
|
38
|
+
# argument strings! For example, if -meaning_of_life is a flag then after this call to Flags.init:
|
39
|
+
# args = [ "-meaning_of_life", "42", "path/to/output/file" ]
|
40
|
+
# Flags.init(args)
|
41
|
+
# the args array will be [ "path/to/output/file" ].
|
42
|
+
def self.init(args=$*)
|
43
|
+
@@init_called = true
|
44
|
+
|
45
|
+
if args.index("--help") || args.index("-help")
|
46
|
+
puts self.help_message
|
47
|
+
exit(0)
|
48
|
+
end
|
49
|
+
@@flags.each do |flag_name, flag|
|
50
|
+
flag_name = "-#{flag.name}"
|
51
|
+
setter = "#{flag.name}="
|
52
|
+
# Use a loop in order to consume all settings for a flag and to keep the last one.
|
53
|
+
while true
|
54
|
+
value = self.extract_value_for_flag(args, flag_name)
|
55
|
+
break if value.nil? # Check for nil because false values are ok for boolean flags.
|
56
|
+
self.send(setter, value)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Defines a new flag of string type, such as:
|
62
|
+
# Flags.define_string(:my_flag_name, "hello world", "An example of a string flag")
|
63
|
+
def self.define_string(name, default_value, description)
|
64
|
+
self.define_flag(name, default_value, description, StringFlag)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Defines a new flag of symbol type, such as:
|
68
|
+
# Flags.define_symbol(:my_flag_name, :a_symbol, "An example of a symbol flag")
|
69
|
+
def self.define_symbol(name, default_value, description)
|
70
|
+
self.define_flag(name, default_value, description, SymbolFlag)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Defines a new flag of integer type, such as:
|
74
|
+
# Flags.define_int(:my_flag_name, 42, "An example of an integer flag")
|
75
|
+
def self.define_int(name, default_value, description)
|
76
|
+
self.define_flag(name, default_value, description, IntFlag)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Defines a new flag of boolean type, such as:
|
80
|
+
# Flags.define_bool(:my_flag_name, true, "An example of a boolean flag")
|
81
|
+
def self.define_bool(name, default_value, description)
|
82
|
+
self.define_flag(name, default_value, description, BoolFlag)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Defines a new flag of float type, such as:
|
86
|
+
# Flags.define_float(:my_flag_name, 3.14, "An example of a float flag")
|
87
|
+
def self.define_float(name, default_value, description)
|
88
|
+
self.define_flag(name, default_value, description, FloatFlag)
|
89
|
+
end
|
90
|
+
|
91
|
+
# TODO: Get rid of the longer define_*_flag() methods and just keep the define_*() ones.
|
92
|
+
# Alias the older, longer methods to the new, shorter ones.
|
93
|
+
class << self
|
94
|
+
alias :define_string_flag :define_string
|
95
|
+
alias :define_symbol_flag :define_symbol
|
96
|
+
alias :define_int_flag :define_int
|
97
|
+
alias :define_bool_flag :define_bool
|
98
|
+
alias :define_float_flag :define_float
|
99
|
+
end
|
100
|
+
|
101
|
+
# Registers a flag validator that checks the range of a flag.
|
102
|
+
def self.register_range_validator(name, range)
|
103
|
+
raise_unless_symbol!(name)
|
104
|
+
raise_unless_flag_defined!(name)
|
105
|
+
flag = @@flags[name]
|
106
|
+
flag.add_validator(RangeValidator.new(range))
|
107
|
+
end
|
108
|
+
|
109
|
+
# Registers a flag validator that ensures that the flag value is one of the allowed values.
|
110
|
+
def self.register_allowed_values_validator(name, *allowed_values)
|
111
|
+
raise_unless_symbol!(name)
|
112
|
+
raise_unless_flag_defined!(name)
|
113
|
+
flag = @@flags[name]
|
114
|
+
flag.add_validator(AllowedValuesValidator.new(allowed_values))
|
115
|
+
end
|
116
|
+
|
117
|
+
# Registers a flag validator that ensures that the flag value is not one of the disallowed values.
|
118
|
+
def self.register_disallowed_values_validator(name, *disallowed_values)
|
119
|
+
raise_unless_symbol!(name)
|
120
|
+
raise_unless_flag_defined!(name)
|
121
|
+
flag = @@flags[name]
|
122
|
+
flag.add_validator(DisallowedValuesValidator.new(disallowed_values))
|
123
|
+
end
|
124
|
+
|
125
|
+
# Registers a custom flag validator that will raise an error with the specified message if proc.call(value)
|
126
|
+
# returns false whenever a value is assigned to the flag.
|
127
|
+
def self.register_custom_validator(name, proc, error_message)
|
128
|
+
raise_unless_symbol!(name)
|
129
|
+
raise_unless_flag_defined!(name)
|
130
|
+
flag = @@flags[name]
|
131
|
+
validator = FlagValidator.new(proc, error_message)
|
132
|
+
flag.add_validator(validator)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Returns the description of a flag, or nil if the given flag is not defined.
|
136
|
+
# TODO: Deprecate and remove?
|
137
|
+
def self.comment_for_flag(name)
|
138
|
+
return nil unless @@flags.key? name.to_sym
|
139
|
+
return @@flags[name.to_sym].description
|
140
|
+
end
|
141
|
+
|
142
|
+
# TODO: how to do aliases for class methods?
|
143
|
+
def self.flags_get_comment(name)
|
144
|
+
self.comment_for_flag(name)
|
145
|
+
end
|
146
|
+
|
147
|
+
def self.flags_get_default_value(name)
|
148
|
+
raise_unless_flag_defined!(name)
|
149
|
+
return @@flags[name.to_sym].default_value
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.flags_is_default(name)
|
153
|
+
raise_unless_flag_defined!(name)
|
154
|
+
return @@flags[name.to_sym].default?
|
155
|
+
end
|
156
|
+
|
157
|
+
# Sets the value of the named flag if the default value has not been overridden. A default value can be
|
158
|
+
# overridden in one of these ways:
|
159
|
+
# 1) By specifying a value for the flag on the command line (i.e. "-x y")
|
160
|
+
# 2) By explicitly changing the value of the flag in the code (i.e. Flags.x = y)
|
161
|
+
# 3) By loading a value for the flag from a hash or yaml file.
|
162
|
+
#
|
163
|
+
# Returns true if the flag value was changed and false if it was not. Raises an error if the named flag does
|
164
|
+
# not exist.
|
165
|
+
def self.set_if_default(name, value)
|
166
|
+
raise_unless_symbol!(name)
|
167
|
+
raise_unless_flag_defined!(name)
|
168
|
+
|
169
|
+
flag = @@flags[name]
|
170
|
+
return false unless flag.default?
|
171
|
+
flag.value = value # NOTE: calling the setter method changes the internal is_default field to false
|
172
|
+
return true
|
173
|
+
end
|
174
|
+
|
175
|
+
# Takes a hash of { flag name => flag value } pairs and calls self.set_if_default() on each pair.
|
176
|
+
def self.set_multiple_if_default(hash)
|
177
|
+
hash.each_pair do |flag_name, flag_value|
|
178
|
+
self.set_if_default(flag_name, flag_value)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# Restores the value of the named flag to its default setting, and returns nil. Raises an error if the named
|
183
|
+
# flag does not exist.
|
184
|
+
def self.restore_default(name)
|
185
|
+
raise_unless_symbol!(name)
|
186
|
+
raise_unless_flag_defined!(name)
|
187
|
+
|
188
|
+
@@flags[name].restore_default
|
189
|
+
nil
|
190
|
+
end
|
191
|
+
|
192
|
+
# Takes an Array or Set of flag names, and calls self.restore_default() on each element.
|
193
|
+
def self.restore_multiple_defaults(names)
|
194
|
+
names.each do |flag_name|
|
195
|
+
self.restore_default(flag_name)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# Restores the values of all flags to their default settings, and returns nil.
|
200
|
+
def self.restore_all_defaults()
|
201
|
+
@@flags.each_pair do |name, flag|
|
202
|
+
flag.restore_default
|
203
|
+
end
|
204
|
+
nil
|
205
|
+
end
|
206
|
+
|
207
|
+
# Dumps the flags, serialized to yaml to the specified io object. Returns a string if io is nil.
|
208
|
+
def self.to_yaml(io=nil)
|
209
|
+
flags = []
|
210
|
+
@@flags.keys.sort { |a, b| a.to_s <=> b.to_s }.each do |name|
|
211
|
+
flag = @@flags[name]
|
212
|
+
flags += [ "-#{name}", flag.value ]
|
213
|
+
end
|
214
|
+
YAML.dump(flags, io)
|
215
|
+
end
|
216
|
+
|
217
|
+
# Serializes the arguments from yaml and returns an array of arguments that can be passed to
|
218
|
+
# init.
|
219
|
+
def self.args_from_yaml(yaml)
|
220
|
+
YAML.load(yaml)
|
221
|
+
end
|
222
|
+
|
223
|
+
# This function takes the @@flags object, and returns a hash of just the key, value pairs
|
224
|
+
def self.to_hash
|
225
|
+
return Hash[@@flags.map { |name, flag| [name, flag.value] }]
|
226
|
+
end
|
227
|
+
|
228
|
+
# Converts the flags to a printable string.
|
229
|
+
def self.to_s
|
230
|
+
flags = []
|
231
|
+
@@flags.keys.sort { |a, b| a.to_s <=> b.to_s }.each do |name|
|
232
|
+
flag = @@flags[name]
|
233
|
+
flag_name = "-#{name}"
|
234
|
+
value = flag.value
|
235
|
+
if value.is_a?(String) # we call inspect() on String flags to take care of embedded quotes, spaces, etc.
|
236
|
+
flags << "-#{name} #{value.inspect}"
|
237
|
+
else
|
238
|
+
flags << "-#{name} #{value.to_s}"
|
239
|
+
end
|
240
|
+
end
|
241
|
+
flags.join(" ")
|
242
|
+
end
|
243
|
+
|
244
|
+
# This override makes it so that the mocha rubygem prints nicer debug messages. The downside
|
245
|
+
# is that now you need to call Flags.to_s to print human-readable strings.
|
246
|
+
def self.inspect
|
247
|
+
"Flags"
|
248
|
+
end
|
249
|
+
|
250
|
+
private
|
251
|
+
|
252
|
+
# Has Flags.init been called already? Flag values should not be read or written prior to calling Flags.init.
|
253
|
+
@@init_called = false
|
254
|
+
|
255
|
+
# Hash of flag name symbols => Flag objects.
|
256
|
+
@@flags = {}
|
257
|
+
|
258
|
+
# Internal method that defines a new flag with the given name, default value, description, and type. Used
|
259
|
+
# by the public define_* and define_*_flag methods.
|
260
|
+
def self.define_flag(name, default_value, description, flag_class)
|
261
|
+
raise_unless_symbol!(name)
|
262
|
+
raise_if_flag_defined!(name)
|
263
|
+
|
264
|
+
# For each flag, we store the file name where it was defined, and print these file names if called with
|
265
|
+
# the --help argument. To get the file names we have to examine the call stack, which is accessible
|
266
|
+
# through the Kernel.caller() method. The caller method returns an array of strings that look like this:
|
267
|
+
# ["./bar.rb:1", "foo.rb:2:in `require'", "foo.rb:2"]
|
268
|
+
#
|
269
|
+
# The caller frame immediately above this one (index 0) is one of the public define_*_flag methods. The
|
270
|
+
# frame above that (index 1) is the location where the Flags.define_*_flag method was called, and is the
|
271
|
+
# one we want. We also strip the leading "./" from the file name if it's present, for improved readability.
|
272
|
+
definition_file = caller[1].split(":")[0].gsub("./", "")
|
273
|
+
flag = flag_class.new(name, default_value, description, definition_file)
|
274
|
+
@@flags[name] = flag
|
275
|
+
|
276
|
+
# TODO: The flag getter and setter methods should call raise_unless_initialized! However, for now
|
277
|
+
# this breaks unit tests so not viable until we add some kind of hook with test/unit that calls Flags.init
|
278
|
+
# with empty args before each test case runs.
|
279
|
+
|
280
|
+
# TODO: Would look cleaner if we used the eigenclass approach and class_eval + code block instead
|
281
|
+
# of instance_eval + string.
|
282
|
+
|
283
|
+
instance_eval "def self.#{name}; @@flags[:#{name.to_s}].value; end"
|
284
|
+
instance_eval "def self.#{name}=(value); @@flags[:#{name.to_s}].value = value; end"
|
285
|
+
end
|
286
|
+
|
287
|
+
# Internal method that undefines a flag. Used by the unit test to clean up state between test cases.
|
288
|
+
def self.undefine_flag(name)
|
289
|
+
raise_unless_symbol!(name)
|
290
|
+
raise_unless_flag_defined!(name)
|
291
|
+
|
292
|
+
@@flags.delete(name)
|
293
|
+
|
294
|
+
eigenclass = class << Flags; self; end
|
295
|
+
eigenclass.class_eval "remove_method :#{name}"
|
296
|
+
eigenclass.class_eval "remove_method :#{name}="
|
297
|
+
end
|
298
|
+
|
299
|
+
# Raises an error if a flag with the given name is already defined, or if the flag name would conflict
|
300
|
+
# with an existing Flags method.
|
301
|
+
def self.raise_if_flag_defined!(name)
|
302
|
+
raise "Flag #{name} already defined" if @@flags.key?(name.to_sym)
|
303
|
+
raise "Flag #{name} conflicts with an internal Flags method" if self.respond_to?(name.to_sym)
|
304
|
+
nil
|
305
|
+
end
|
306
|
+
|
307
|
+
# Raises an error unless a flag with the given name is defined.
|
308
|
+
def self.raise_unless_flag_defined!(name)
|
309
|
+
raise "Flag #{name} not defined" unless @@flags.key?(name.to_sym)
|
310
|
+
nil
|
311
|
+
end
|
312
|
+
|
313
|
+
# Raises an error unless Flags.init has been called at least once. Useful for catching subtle "I forgot to
|
314
|
+
# call Flags.init at the start of my program" bugs.
|
315
|
+
def self.raise_unless_initialized!()
|
316
|
+
raise "Flags.init has not been called" unless @@init_called
|
317
|
+
end
|
318
|
+
|
319
|
+
def self.raise_unless_symbol!(name)
|
320
|
+
raise ArgumentError, "Flag name must be a symbol but is a #{name.class}" unless name.is_a?(Symbol)
|
321
|
+
end
|
322
|
+
|
323
|
+
def self.extract_value_for_flag(args, flag)
|
324
|
+
index = args.index(flag)
|
325
|
+
if index
|
326
|
+
value = args[index+1]
|
327
|
+
args[index..(index+1)] = [] # delete from args
|
328
|
+
return value
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
def self.help_message
|
333
|
+
help = "Known command line flags:\n\n"
|
334
|
+
max_name_length = @@flags.values.map { |flag| flag.name.to_s.length }.max
|
335
|
+
# Sort the flags by the location in which they are defined (primary) and flag name (secondary).
|
336
|
+
definition_file_to_flags = Hash.new { |h, k| h[k] = [] }
|
337
|
+
@@flags.each_pair do |name, flag|
|
338
|
+
definition_file_to_flags[flag.definition_file].push name
|
339
|
+
end
|
340
|
+
|
341
|
+
definition_file_to_flags.keys.sort.each do |file|
|
342
|
+
flags = definition_file_to_flags[file]
|
343
|
+
help << "Defined in #{file}:\n"
|
344
|
+
flags.sort { |a, b| a.to_s <=> b.to_s }.each do |flag_name|
|
345
|
+
flag = @@flags[flag_name]
|
346
|
+
help << " -#{flag_name.to_s.ljust(max_name_length+1)} (#{flag.type}) #{flag.description} "\
|
347
|
+
"(Default: #{flag.default_value.inspect})\n"
|
348
|
+
end
|
349
|
+
help << "\n"
|
350
|
+
end
|
351
|
+
help
|
352
|
+
end
|
353
|
+
|
354
|
+
# A subclass of ArgumentError that's raised when an invalid value is assigned to a flag.
|
355
|
+
class InvalidFlagValueError < ArgumentError
|
356
|
+
def initialize(flag_name, flag_value, error_message)
|
357
|
+
super("Flag value #{flag_value.inspect} for flag -#{flag_name.to_s} is invalid: #{error_message}")
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
# A FlagValidator can check whether a flag's value is valid (with validity implicitly defined via a provided
|
362
|
+
# callback), and raise an error if the validation check fails.
|
363
|
+
class FlagValidator
|
364
|
+
def initialize(proc, error_message)
|
365
|
+
@proc = proc
|
366
|
+
@error_message = error_message
|
367
|
+
end
|
368
|
+
|
369
|
+
def validate!(flag_name, flag_value)
|
370
|
+
raise InvalidFlagValueError.new(flag_name, flag_value, @error_message) unless @proc.call(flag_value)
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
# A validator that raises an error if the class of the flag's value is not one of the expected classes.
|
375
|
+
class ClassValidator < FlagValidator
|
376
|
+
def initialize(*expected_value_classes)
|
377
|
+
expected_value_classes = expected_value_classes.to_a.flatten
|
378
|
+
proc = Proc.new { |flag_value| expected_value_classes.include?(flag_value.class) }
|
379
|
+
error_message = "unexpected value class, expecting one of [#{expected_value_classes.join(',')}]"
|
380
|
+
super(proc, error_message)
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
# A validator that raises an error if the flag's value is outside the given Range.
|
385
|
+
# No type checking is performed (that's the job of the ClassValidator).
|
386
|
+
class RangeValidator < FlagValidator
|
387
|
+
def initialize(range)
|
388
|
+
proc = Proc.new { |flag_value| range.include?(flag_value) }
|
389
|
+
error_message = "value out of range! Valid range is #{range.inspect}"
|
390
|
+
super(proc, error_message)
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
# A validator that raises an error if the flag's value is not in the provided set of allowed values.
|
395
|
+
class AllowedValuesValidator < FlagValidator
|
396
|
+
def initialize(*allowed_values)
|
397
|
+
allowed_values = allowed_values.to_a.flatten
|
398
|
+
proc = Proc.new { |flag_value| allowed_values.include?(flag_value) }
|
399
|
+
error_message = "illegal value, expecting one of [#{allowed_values.join(',')}]"
|
400
|
+
super(proc, error_message)
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
# A validator that raises an error if the flag's value is in the provided set of disallowed values.
|
405
|
+
class DisallowedValuesValidator < FlagValidator
|
406
|
+
def initialize(*disallowed_values)
|
407
|
+
disallowed_values = disallowed_values.to_a.flatten
|
408
|
+
proc = Proc.new { |flag_value| !disallowed_values.include?(flag_value) }
|
409
|
+
error_message = "illegal value, may not be one of [#{disallowed_values.join(',')}]"
|
410
|
+
super(proc, error_message)
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
# A Flag represents everything we know about a flag - its name, value, default value, description, where
|
415
|
+
# it was defined, whether the default value has been explicitly modified, and an optional callback to
|
416
|
+
# validate the flag.
|
417
|
+
class Flag
|
418
|
+
attr_reader :type # the type of flag, as a symbol
|
419
|
+
attr_reader :name # the name of the flag
|
420
|
+
attr_reader :value # the current value of the flag
|
421
|
+
attr_reader :default_value # the default value of the flag
|
422
|
+
attr_reader :is_explicit # false unless the flag's value has been explicitly changed from default.
|
423
|
+
# NOTE: if a flag is explicitly set to the default value, this will be true!
|
424
|
+
attr_reader :description # a string description of the flag
|
425
|
+
attr_reader :definition_file # The file name where the flag was defined
|
426
|
+
|
427
|
+
# Initializes the Flag. Arguments:
|
428
|
+
# type - the type of this flag, as one of the symbols [ :string, :symbol, :int, :float, :bool ]
|
429
|
+
# name - the name of this flag, as a symbol
|
430
|
+
# default_value - the default value of the flag. The current value always equals the default when the
|
431
|
+
# Flag object is initialized.
|
432
|
+
# description - a String describing the flag.
|
433
|
+
# definition_file - a String describing the path to the file where this flag was defined.
|
434
|
+
# validators - a list of FlagValidator objects. Additional validators can be added after the flag is
|
435
|
+
# constructed, and all of the registered validators are checked whenever a flag assignment is
|
436
|
+
# performed.
|
437
|
+
def initialize(type, name, default_value, description, definition_file, validators)
|
438
|
+
@type = type
|
439
|
+
@name = name
|
440
|
+
@default_value = default_value
|
441
|
+
@description = description
|
442
|
+
@definition_file = definition_file
|
443
|
+
@validators = validators
|
444
|
+
self.value = default_value # use the public setter method which performs type checking
|
445
|
+
@is_explicit = false # @is_explicit must be set to false AFTER calling the value= method
|
446
|
+
end
|
447
|
+
|
448
|
+
# Sets the value of the flag. The new_value argument can be a String or the appropriate type for the flag
|
449
|
+
# (i.e. a Float for FloatFlag). If it's a String, an attempt will be made to convert to the correct type.
|
450
|
+
def value=(new_value)
|
451
|
+
new_value = value_from_string(new_value) if new_value.is_a?(String)
|
452
|
+
validate!(new_value)
|
453
|
+
@is_explicit = true
|
454
|
+
@value = new_value
|
455
|
+
end
|
456
|
+
|
457
|
+
# Restores the default value of the flag.
|
458
|
+
def restore_default()
|
459
|
+
@is_explicit = false
|
460
|
+
@value = @default_value
|
461
|
+
end
|
462
|
+
|
463
|
+
# Returns true if the flag has been explicitly set.
|
464
|
+
alias :explicit? :is_explicit
|
465
|
+
|
466
|
+
# Returns true if the flag has not been explicitly set.
|
467
|
+
def default?; return !explicit?; end
|
468
|
+
|
469
|
+
# Adds a new validator object to this flag and immediately validates the current value against it.
|
470
|
+
def add_validator(validator)
|
471
|
+
@validators.push validator
|
472
|
+
validate!(@value)
|
473
|
+
end
|
474
|
+
|
475
|
+
private
|
476
|
+
|
477
|
+
# Method for subclasses to implement that converts from a String argument to the flag's internal data
|
478
|
+
# type. Subclasses may throw an ArgumentError if the input is malformed. Subclasses return the string
|
479
|
+
# itself if they cannot convert, which will be caught by the Class Validator
|
480
|
+
def value_from_string(string)
|
481
|
+
raise NotImplementedError, "Subclass must implement value_from_string()"
|
482
|
+
end
|
483
|
+
|
484
|
+
def validate!(value)
|
485
|
+
@validators.each { |validator| validator.validate!(@name, value) }
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
# A flag with a string value.
|
490
|
+
class StringFlag < Flag
|
491
|
+
def initialize(name, default_value, description, definition_file)
|
492
|
+
super(:string, name, default_value, description, definition_file, [ClassValidator.new(String)])
|
493
|
+
end
|
494
|
+
|
495
|
+
private
|
496
|
+
def value_from_string(string); return string.to_s; end
|
497
|
+
end
|
498
|
+
|
499
|
+
# A flag with a symbol value.
|
500
|
+
class SymbolFlag < Flag
|
501
|
+
def initialize(name, default_value, description, definition_file)
|
502
|
+
super(:symbol, name, default_value, description, definition_file, [ClassValidator.new(Symbol)])
|
503
|
+
end
|
504
|
+
|
505
|
+
private
|
506
|
+
def value_from_string(string); return string.to_sym; end
|
507
|
+
end
|
508
|
+
|
509
|
+
# A flag with an integer value.
|
510
|
+
class IntFlag < Flag
|
511
|
+
def initialize(name, default_value, description, definition_file)
|
512
|
+
super(:int, name, default_value, description, definition_file,
|
513
|
+
[ClassValidator.new(Integer, Bignum, Fixnum)])
|
514
|
+
end
|
515
|
+
|
516
|
+
private
|
517
|
+
def value_from_string(string); Integer(string) rescue string; end
|
518
|
+
end
|
519
|
+
|
520
|
+
# A flag with a boolean value.
|
521
|
+
class BoolFlag < Flag
|
522
|
+
def initialize(name, default_value, description, definition_file)
|
523
|
+
super(:bool, name, default_value, description, definition_file,
|
524
|
+
[ClassValidator.new(TrueClass, FalseClass)])
|
525
|
+
end
|
526
|
+
|
527
|
+
private
|
528
|
+
STRING_TO_BOOL_VALUE = { 'true' => true, 'false' => false }
|
529
|
+
|
530
|
+
def value_from_string(string)
|
531
|
+
result = STRING_TO_BOOL_VALUE[string.downcase]
|
532
|
+
return result.nil? ? string : result
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
# A flag with a floating-point numeric value.
|
537
|
+
class FloatFlag < Flag
|
538
|
+
def initialize(name, default_value, description, definition_file)
|
539
|
+
super(:float, name, default_value, description, definition_file, [ClassValidator.new(Float)])
|
540
|
+
end
|
541
|
+
|
542
|
+
private
|
543
|
+
def value_from_string(string); return Float(string) rescue string; end
|
544
|
+
end
|
545
|
+
end
|
data/test/flags_test.rb
ADDED
@@ -0,0 +1,277 @@
|
|
1
|
+
# Copyright © 2011 Ooyala, Inc.
|
2
|
+
# Tests for flags.rb - To run, execute ruby test/flags_test.rb from the root gem directory
|
3
|
+
require "rubygems"
|
4
|
+
require "shoulda"
|
5
|
+
require "mocha"
|
6
|
+
|
7
|
+
require "flags"
|
8
|
+
|
9
|
+
class FlagsTest < Test::Unit::TestCase
|
10
|
+
# Tests for undefine functionality used in the teardown block of all other tests. We want to make sure
|
11
|
+
# those work before running any other tests, so these tests are purposefully placed outside a context.
|
12
|
+
# Notice that these tests manually do their own teardown.
|
13
|
+
def test_undefine_flag
|
14
|
+
assert_raise(NoMethodError) { Flags.my_flag_name }
|
15
|
+
Flags.define_int_flag(:my_flag_name, 42, "Description")
|
16
|
+
assert_equal 42, Flags.my_flag_name
|
17
|
+
Flags.send(:undefine_flag, :my_flag_name)
|
18
|
+
assert_raise(NoMethodError) { Flags.my_flag_name }
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_access_flag_names
|
22
|
+
flag_names = Flags.send(:class_variable_get, "@@flags").map { |name, flag| name }
|
23
|
+
assert_equal 0, flag_names.size
|
24
|
+
Flags.define_int_flag(:my_flag_name, 42, "Description")
|
25
|
+
flag_names = Flags.send(:class_variable_get, "@@flags").map { |name, flag| name }
|
26
|
+
assert_equal [ :my_flag_name ], flag_names
|
27
|
+
Flags.send(:undefine_flag, :my_flag_name)
|
28
|
+
end
|
29
|
+
|
30
|
+
context "Flags" do
|
31
|
+
teardown do # Undefine all flags after running a test
|
32
|
+
flag_names = Flags.send(:class_variable_get, "@@flags").map { |name, flag| name }
|
33
|
+
flag_names.each do |flag_name|
|
34
|
+
Flags.send(:undefine_flag, flag_name)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
should "define_string_flag" do
|
39
|
+
Flags.define_string_flag(:foo, "bar", "Test string flag")
|
40
|
+
assert_equal "bar", Flags.foo
|
41
|
+
end
|
42
|
+
|
43
|
+
should "define_symbol_flag" do
|
44
|
+
Flags.define_symbol_flag(:foo, :bar, "Test symbol flag")
|
45
|
+
Flags.define_symbol_flag(:foo2, "baz", "Test symbol flag from string conversion")
|
46
|
+
assert_equal :bar, Flags.foo
|
47
|
+
assert_equal :baz, Flags.foo2
|
48
|
+
end
|
49
|
+
|
50
|
+
should "define_int_flag" do
|
51
|
+
Flags.define_int_flag(:bar, 1, "Test int flag")
|
52
|
+
Flags.define_int_flag(:baz, "2", "Test int flag from string conversion")
|
53
|
+
assert_equal 1, Flags.bar
|
54
|
+
assert_equal 2, Flags.baz
|
55
|
+
end
|
56
|
+
|
57
|
+
should "define_bool_flag" do
|
58
|
+
Flags.define_bool_flag(:true_bool_flag, true, "Test true bool flag")
|
59
|
+
Flags.define_bool_flag(:false_bool_flag, false, "Test false bool flag")
|
60
|
+
Flags.define_bool_flag(:true_bool_flag2, "true", "Test true bool flag from string conversion")
|
61
|
+
Flags.define_bool_flag(:false_bool_flag2, "false", "Test false bool flag from string conversion")
|
62
|
+
assert Flags.true_bool_flag
|
63
|
+
assert Flags.true_bool_flag2
|
64
|
+
assert !Flags.false_bool_flag
|
65
|
+
assert !Flags.false_bool_flag2
|
66
|
+
end
|
67
|
+
|
68
|
+
should "define_float_flag" do
|
69
|
+
Flags.define_float_flag(:float_flag, 2.0, "Test float flag")
|
70
|
+
Flags.define_float_flag(:float_flag2, "3.0", "Test float flag from string conversion")
|
71
|
+
assert_equal 2.0, Flags.float_flag
|
72
|
+
assert_equal 3.0, Flags.float_flag2
|
73
|
+
end
|
74
|
+
|
75
|
+
should "return flag comment" do
|
76
|
+
Flags.define_string_flag(:foo, "foobar", "Test string flag")
|
77
|
+
assert_equal "Test string flag", Flags.flags_get_comment(:foo)
|
78
|
+
end
|
79
|
+
|
80
|
+
should "return default value" do
|
81
|
+
Flags.define_string_flag(:foo, "foobar", "Test string flag")
|
82
|
+
Flags.foo = "baz"
|
83
|
+
assert_equal "baz", Flags.foo
|
84
|
+
assert_equal "foobar", Flags.flags_get_default_value(:foo)
|
85
|
+
end
|
86
|
+
|
87
|
+
should "initialize flags with init" do
|
88
|
+
Flags.define_float_flag(:bar, 2.0, "")
|
89
|
+
Flags.define_string_flag(:foo, "foobar", "")
|
90
|
+
Flags.define_int_flag(:baz, 3, "")
|
91
|
+
Flags.init(["-bar", 3.0, "-foo", "you rock"])
|
92
|
+
assert_equal 3.0, Flags.bar
|
93
|
+
assert_equal "you rock", Flags.foo
|
94
|
+
assert_equal 3, Flags.baz
|
95
|
+
end
|
96
|
+
|
97
|
+
should "remove known flags from args array but leave non-flags alone" do
|
98
|
+
Flags.define_int_flag(:foo, 41, "")
|
99
|
+
args = [ "-foo", "42", "hello world" ]
|
100
|
+
Flags.init(args)
|
101
|
+
assert_equal [ "hello world" ], args
|
102
|
+
end
|
103
|
+
|
104
|
+
should "init bool flag with true default" do
|
105
|
+
Flags.define_bool_flag(:bar, true, "")
|
106
|
+
Flags.init(["-bar", false])
|
107
|
+
assert !Flags.bar
|
108
|
+
end
|
109
|
+
|
110
|
+
should "init bool flag with true default and string initial value" do
|
111
|
+
Flags.define_bool_flag(:bar, true, "")
|
112
|
+
Flags.init(["-bar", "false"])
|
113
|
+
assert !Flags.bar
|
114
|
+
end
|
115
|
+
|
116
|
+
should "pick last flag value when multiple args are present" do
|
117
|
+
Flags.define_bool_flag(:bar, true, "")
|
118
|
+
Flags.define_int_flag(:baz, 3, "")
|
119
|
+
Flags.init(["-bar", "false", "-baz", 4, "-bar", "true", "-baz" , 5])
|
120
|
+
assert Flags.bar
|
121
|
+
assert_equal 5, Flags.baz
|
122
|
+
end
|
123
|
+
|
124
|
+
should "serialize to string" do
|
125
|
+
Flags.define_float_flag(:bar, 2.0, "bla")
|
126
|
+
Flags.define_string_flag(:baz, "you rock", "comment \"bla\" 'bla'")
|
127
|
+
Flags.define_int_flag(:foo, 3, "")
|
128
|
+
Flags.define_bool_flag(:bool, true, "")
|
129
|
+
assert_equal "-bar 2.0 -baz \"you rock\" -bool true -foo 3", Flags.to_s
|
130
|
+
end
|
131
|
+
|
132
|
+
should "override the default state flags with init" do
|
133
|
+
Flags.define_float_flag(:bar, 2.0, "")
|
134
|
+
Flags.define_string_flag(:foo, "foobar", "")
|
135
|
+
Flags.define_int_flag(:baz, 3, "")
|
136
|
+
Flags.init(["-bar", 3.0, "-foo", "you rock"])
|
137
|
+
assert_equal 3.0, Flags.bar
|
138
|
+
assert_equal "you rock", Flags.foo
|
139
|
+
assert_equal 3, Flags.baz
|
140
|
+
end
|
141
|
+
|
142
|
+
should "serialize to/from yaml" do
|
143
|
+
Flags.define_float_flag(:bar, 2.0, "")
|
144
|
+
Flags.define_string_flag(:foo, "foobar", "")
|
145
|
+
args = Flags.args_from_yaml Flags.to_yaml
|
146
|
+
assert_equal ["-bar", 2.0, "-foo", "foobar"], args
|
147
|
+
end
|
148
|
+
|
149
|
+
context "default values" do
|
150
|
+
should "flags_is_default should return true initially" do
|
151
|
+
Flags.define_int_flag(:foo, 42, "")
|
152
|
+
assert Flags.flags_is_default(:foo)
|
153
|
+
end
|
154
|
+
|
155
|
+
should "flags_is_default should return false after flag assignment" do
|
156
|
+
Flags.define_int_flag(:foo, 42, "")
|
157
|
+
Flags.foo = 43
|
158
|
+
assert !Flags.flags_is_default(:foo)
|
159
|
+
end
|
160
|
+
|
161
|
+
should "flags_is_default should return false after loading flags from command line" do
|
162
|
+
Flags.define_int_flag(:foo, 42, "")
|
163
|
+
Flags.define_int_flag(:bar, 43, "")
|
164
|
+
Flags.init(["-foo", "41"])
|
165
|
+
assert !Flags.flags_is_default(:foo)
|
166
|
+
assert Flags.flags_is_default(:bar)
|
167
|
+
end
|
168
|
+
|
169
|
+
should "set_if_default should change default value" do
|
170
|
+
Flags.define_int_flag(:foo, 42, "")
|
171
|
+
Flags.set_if_default(:foo, 43)
|
172
|
+
assert_equal 43, Flags.foo
|
173
|
+
assert !Flags.flags_is_default(:foo)
|
174
|
+
end
|
175
|
+
|
176
|
+
should "set_if_default should not change non-default value" do
|
177
|
+
Flags.define_int_flag(:foo, 42, "")
|
178
|
+
Flags.foo = 41
|
179
|
+
Flags.set_if_default(:foo, 43)
|
180
|
+
assert_equal 41, Flags.foo
|
181
|
+
end
|
182
|
+
|
183
|
+
should "restore default value" do
|
184
|
+
Flags.define_int_flag(:foo, 42, "")
|
185
|
+
Flags.foo = 41
|
186
|
+
Flags.restore_default(:foo)
|
187
|
+
assert_equal 42, Flags.foo
|
188
|
+
assert Flags.flags_is_default(:foo)
|
189
|
+
end
|
190
|
+
|
191
|
+
should "restore all default values" do
|
192
|
+
Flags.define_int_flag(:foo, 42, "")
|
193
|
+
Flags.define_int_flag(:bar, 43, "")
|
194
|
+
Flags.foo, Flags.bar = 41, 40
|
195
|
+
Flags.restore_all_defaults()
|
196
|
+
assert_equal 42, Flags.foo
|
197
|
+
assert_equal 43, Flags.bar
|
198
|
+
assert Flags.flags_is_default(:foo) && Flags.flags_is_default(:bar)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
context "flag validators" do
|
203
|
+
should "fail expected class validation" do
|
204
|
+
Flags.define_int_flag(:foo, 1, "...")
|
205
|
+
assert_raise(Flags::InvalidFlagValueError) { Flags.foo = "not an int" }
|
206
|
+
Flags.define_float_flag(:bar, 1.0, "...")
|
207
|
+
assert_raise(Flags::InvalidFlagValueError) { Flags.bar = 1 }
|
208
|
+
Flags.define_bool_flag(:baz, true, "...")
|
209
|
+
assert_raise(Flags::InvalidFlagValueError) { Flags.baz = :hi_there }
|
210
|
+
# Converting an empty string to a symbol is an error in Ruby 1.8 but not 1.9
|
211
|
+
if RUBY_VERSION[0..2] == '1.8'
|
212
|
+
Flags.define_symbol_flag(:boo, :a, "...")
|
213
|
+
assert_raise(ArgumentError) { Flags.boo = "" }
|
214
|
+
end
|
215
|
+
Flags.define_string_flag(:faz, "hello world", "...")
|
216
|
+
assert_raise(Flags::InvalidFlagValueError) { Flags.faz = 123 }
|
217
|
+
end
|
218
|
+
|
219
|
+
should "register numeric range validators" do
|
220
|
+
# A range open on the 'max' end
|
221
|
+
Flags.define_int_flag(:foo, 1, "Must be greater than or equal to 0")
|
222
|
+
positive_infinity = 1.0 / 0 # TODO: is there a better way to get a reference to the Infinity constant?
|
223
|
+
Flags.register_range_validator(:foo, 0..positive_infinity)
|
224
|
+
Flags.foo = 2 ** 128
|
225
|
+
Flags.foo = 0
|
226
|
+
assert_raise(Flags::InvalidFlagValueError) { Flags.foo = -1 }
|
227
|
+
assert_equal 0, Flags.foo
|
228
|
+
|
229
|
+
# A range open on the 'min' end
|
230
|
+
Flags.define_int_flag(:bar, -1, "Must be less than or equal to 0")
|
231
|
+
negative_infinity = -1.0 / 0
|
232
|
+
Flags.register_range_validator(:bar, negative_infinity..0)
|
233
|
+
Flags.bar = -2 ** 127
|
234
|
+
Flags.bar = 0
|
235
|
+
assert_raise(Flags::InvalidFlagValueError) { Flags.bar = 1 }
|
236
|
+
assert_equal 0, Flags.bar
|
237
|
+
|
238
|
+
# A range closed on both ends
|
239
|
+
Flags.define_int_flag(:boo, 0, "Must be between -2 and 2")
|
240
|
+
Flags.register_range_validator(:boo, -2..2)
|
241
|
+
Flags.boo = -2
|
242
|
+
Flags.boo = 2
|
243
|
+
assert_raise(Flags::InvalidFlagValueError) { Flags.boo = -3 }
|
244
|
+
assert_raise(Flags::InvalidFlagValueError) { Flags.boo = 3 }
|
245
|
+
assert_equal 2, Flags.boo
|
246
|
+
end
|
247
|
+
|
248
|
+
should "register allowed values validator" do
|
249
|
+
Flags.define_symbol_flag(:foo, :north, "Can be one of [:north, :south, :east, :west]")
|
250
|
+
Flags.register_allowed_values_validator(:foo, [:north, :south, :east, :west])
|
251
|
+
assert_raise(Flags::InvalidFlagValueError) { Flags.foo = :up }
|
252
|
+
# One more time and use the variable args version this time
|
253
|
+
Flags.define_string_flag(:bar, "left", "Can be one of (left, right)")
|
254
|
+
Flags.register_allowed_values_validator(:bar, "left", "right")
|
255
|
+
assert_raise(Flags::InvalidFlagValueError) { Flags.bar = "down" }
|
256
|
+
end
|
257
|
+
|
258
|
+
should "register disallowed values validator" do
|
259
|
+
Flags.define_symbol_flag(:foo, :company, "Cannot be any of [:competitor1, :competitor2]")
|
260
|
+
Flags.register_disallowed_values_validator(:foo, [:competitor1, :competitor2])
|
261
|
+
Flags.foo = :client
|
262
|
+
assert_raise(Flags::InvalidFlagValueError) { Flags.foo = :competitor1 }
|
263
|
+
assert_equal :client, Flags.foo
|
264
|
+
end
|
265
|
+
|
266
|
+
should "register custom validator" do
|
267
|
+
Flags.define_int_flag(:even_number, 2, "Even numbers only!")
|
268
|
+
Flags.register_custom_validator(:even_number,
|
269
|
+
Proc.new { |flag_value| flag_value % 2 == 0 },
|
270
|
+
"Flag value must be an even integer")
|
271
|
+
Flags.even_number = 10
|
272
|
+
assert_raise(Flags::InvalidFlagValueError) { Flags.even_number = 11 }
|
273
|
+
assert_equal 10, Flags.even_number
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: flags
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 25
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 1
|
10
|
+
version: 0.1.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Ooyala, Inc.
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-03-16 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: Flags is a framework for Ruby which allows the definition of command-line flags, which are parsed in and can be accessed smartly from within your Ruby code. This framework allows for numerous different flag types, and takes care of the process of type conversion and flag validation (type and value checking).
|
23
|
+
email: rubygems@ooyala.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files: []
|
29
|
+
|
30
|
+
files:
|
31
|
+
- README.markdown
|
32
|
+
- LICENSE
|
33
|
+
- lib/flags.rb
|
34
|
+
- test/flags_test.rb
|
35
|
+
has_rdoc: true
|
36
|
+
homepage: https://github.com/ooyala/flags
|
37
|
+
licenses: []
|
38
|
+
|
39
|
+
post_install_message:
|
40
|
+
rdoc_options: []
|
41
|
+
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
none: false
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
hash: 3
|
50
|
+
segments:
|
51
|
+
- 0
|
52
|
+
version: "0"
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
hash: 3
|
59
|
+
segments:
|
60
|
+
- 0
|
61
|
+
version: "0"
|
62
|
+
requirements: []
|
63
|
+
|
64
|
+
rubyforge_project:
|
65
|
+
rubygems_version: 1.5.2
|
66
|
+
signing_key:
|
67
|
+
specification_version: 3
|
68
|
+
summary: A simple command-line flag framework.
|
69
|
+
test_files: []
|
70
|
+
|