usage 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +75 -0
- data/LICENSE +1 -0
- data/README +219 -0
- data/TODO +2 -0
- data/lib/Usage.rb +1009 -0
- data/samples/Sample1.rb +9 -0
- data/samples/Sample2.rb +9 -0
- data/samples/Sample3.rb +14 -0
- data/samples/Sample4.rb +21 -0
- data/samples/Sample5.rb +13 -0
- data/samples/Sample6.rb +11 -0
- data/samples/Sample7.rb +8 -0
- data/samples/Sample8.rb +8 -0
- data/samples/sample10.rb +9 -0
- data/samples/sample11.rb +9 -0
- data/samples/sample9.rb +9 -0
- data/tests/TC_Usage.rb +521 -0
- metadata +65 -0
data/CHANGES
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
======== Version 0.3 ==============================================================
|
2
|
+
|
3
|
+
The biggest change in version 0.3 is that the argument type architecture is
|
4
|
+
now pluggable. Users can add their own typed argument parsers. Along with
|
5
|
+
doing that, I have added file argument types:
|
6
|
+
|
7
|
+
< - A file that will be read
|
8
|
+
> - A file that will be written to
|
9
|
+
<< - A file that will be read in by readlines
|
10
|
+
>> - A file that will be appended to
|
11
|
+
>? - A file that will be written to but the user is prompted if it already
|
12
|
+
exists
|
13
|
+
>>? - A file that will be appended to but the user is prompted if the file
|
14
|
+
doesn't exist
|
15
|
+
|
16
|
+
Lastly I added the ability to use a block argument to the Usage call so that
|
17
|
+
clean-up of objects created during parsing can be safely cleaned-up at the end
|
18
|
+
of the usage block.
|
19
|
+
|
20
|
+
The simplest example to illustrate all of the above attributes is a very
|
21
|
+
short copy program that has much more error checking (nice pretty error
|
22
|
+
checking) than your average short ruby copy script.
|
23
|
+
|
24
|
+
require "usage"
|
25
|
+
|
26
|
+
Usage.new "<infile >outfile" do |u|
|
27
|
+
u.outfile.write(u.infile.read)
|
28
|
+
end
|
29
|
+
|
30
|
+
A more complex example (taken from the actual built-in writable file code) is
|
31
|
+
shows how you can add a type of your own:
|
32
|
+
|
33
|
+
#
|
34
|
+
# This example is taken from the built-in class for arguments that are writable files
|
35
|
+
# that don't want to be overwritten
|
36
|
+
#
|
37
|
+
|
38
|
+
# first define the file exists exception class
|
39
|
+
class FileOutputExistsError < UsageMod::Error
|
40
|
+
attr_reader :filename
|
41
|
+
def initialize(filename)
|
42
|
+
@filename = filename
|
43
|
+
super("output file exists: '#{filename}'")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# next define the argument parser plugin
|
48
|
+
class FileOutputQueryPlugin < UsageMod::ArgumentParserPlugin
|
49
|
+
def initialize(usage_ui, str)
|
50
|
+
if FileTest.exist?(str) then
|
51
|
+
raise FileOutputExistsError.new(str) if usage_ui.ask_yes_no(OVERWRITE_QUERY % str, NO_RESPONSE) == NO_RESPONSE
|
52
|
+
end
|
53
|
+
@value = File.open(str, "w")
|
54
|
+
end
|
55
|
+
|
56
|
+
def close
|
57
|
+
@value.close
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# lastly attach that parser to the character sequence '>?'
|
62
|
+
UsageMod::Base.add_type_handler(">?", FileOutputQueryPlugin)
|
63
|
+
|
64
|
+
After putting the above code into a library that you require, you do the following:
|
65
|
+
|
66
|
+
require "usage"
|
67
|
+
require "mycustomtype"
|
68
|
+
|
69
|
+
Usage.new "<infile >?outfile" do |u|
|
70
|
+
u.outfile.write(u.infile.read)
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
Check out the README for a complete description of the how usage works.
|
75
|
+
|
data/LICENSE
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
This is released under the same terms as the Ruby license.
|
data/README
ADDED
@@ -0,0 +1,219 @@
|
|
1
|
+
OVERVIEW
|
2
|
+
|
3
|
+
The usage library is a library that is allows command line scripts to be written with
|
4
|
+
a minimum of memorization necessary to access the arguments. I wrote it because I was
|
5
|
+
tired of copying code from other command line programs that used GetOptLong. This was
|
6
|
+
started before there was a profusion of different command line libraries. Even with this
|
7
|
+
profusion, there is still some memorization that goes along with how to set up the
|
8
|
+
framework process the arguments.
|
9
|
+
|
10
|
+
LIMITATIONS
|
11
|
+
|
12
|
+
This library really intended for simplish command line programs. It is not really
|
13
|
+
capable of handling complex command line requirements like commands with sub-commands
|
14
|
+
and the like (like CVS or SVN). It is really intended for that quick command that you
|
15
|
+
are whipping up to do a simple task. It can grow with you for a while but at some
|
16
|
+
point it may not be up to the task.
|
17
|
+
|
18
|
+
USAGE ON USAGE
|
19
|
+
|
20
|
+
A note on terminology. When I mention "arguments" it means command line arguments that
|
21
|
+
are not prefixed by a "-" option. When I mention options, I mean those things following
|
22
|
+
the "-". Options themselves can have arguments as well. So we have:
|
23
|
+
|
24
|
+
program arguments - those non option arguments
|
25
|
+
options - those dash things
|
26
|
+
option arguments - those things that are after but associated with options
|
27
|
+
|
28
|
+
1. Simple Usage
|
29
|
+
|
30
|
+
The only thing you have remember to use usage are how commands are usually documented.
|
31
|
+
First you need to require the usage library:
|
32
|
+
|
33
|
+
require "Usage"
|
34
|
+
|
35
|
+
Then set up the usage string for the command:
|
36
|
+
|
37
|
+
usage = Usage.new "infile outfile"
|
38
|
+
|
39
|
+
The above would be a command with two require arguments: an input file and an output file.
|
40
|
+
To access those arguments, you just need to use the usage variable that was created and
|
41
|
+
send the .infile or .outfile message to them.
|
42
|
+
|
43
|
+
File.open(usage.infile) do |fi|
|
44
|
+
File.open(usage.outfile, "w") do |fo|
|
45
|
+
fo.write(fi.read)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
If the user doesn't supply the correct number of arguments, the program exits with an error
|
50
|
+
and the usage for the program (hence the libraries name).
|
51
|
+
|
52
|
+
PROGRAM: test.rb
|
53
|
+
ERROR: too few arguments 2 expected, 0 given
|
54
|
+
|
55
|
+
USAGE: test.rb infile outfile
|
56
|
+
|
57
|
+
2. Lists of files (...)
|
58
|
+
|
59
|
+
You can write a program that accepts a list of files by using elipses appended to an
|
60
|
+
argument (the following program concatenates the input files into one output file).
|
61
|
+
|
62
|
+
usage = Usage.new "outfile infiles..."
|
63
|
+
|
64
|
+
File.open(usage.outfile, "w") do |fo|
|
65
|
+
usage.infiles.each do |infile|
|
66
|
+
File.open(usage.infile) { |fi| fo.write(fi.read)}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
3. Optional arguments
|
71
|
+
|
72
|
+
You can have optional arguments by surounding them in square brackets.
|
73
|
+
|
74
|
+
usage = Usage.new "[optional_arg] required_arg"
|
75
|
+
|
76
|
+
These are accessed in the standard way
|
77
|
+
|
78
|
+
usage.optional_arg # this is nil if it is not given by the user
|
79
|
+
|
80
|
+
usage.required_arg
|
81
|
+
|
82
|
+
4. Options
|
83
|
+
|
84
|
+
You can have dash options that are either required or optional. Options can also have
|
85
|
+
arguments associated with them.
|
86
|
+
|
87
|
+
usage = Usage.new "[-y] [-x excluded_tags] (-z ztag) (-w warning_arg) files..."
|
88
|
+
|
89
|
+
The options are accessed with "dash_" prefixing the option so that the -y is accessed
|
90
|
+
via .dash_y. The -x can be accessed either with #dash_x (which would be either nil or
|
91
|
+
true) or #excluded_tags (which would be either nil or the argument for the -x option).
|
92
|
+
The -z option is required and has one argument, also the -w option is also required.
|
93
|
+
They can appear in any order (-z option first or -w option first). The optional arguments
|
94
|
+
can appear either before, interspersed with, or after the required options.
|
95
|
+
|
96
|
+
5. Long Options
|
97
|
+
|
98
|
+
You can also have long options by including lines following the initial usage line that
|
99
|
+
associates the short options with the long ones. Example below:
|
100
|
+
|
101
|
+
usage = Usage.new "-x files...", <<EOT
|
102
|
+
-x,--exclusive specifies exclusive access to the files
|
103
|
+
EOT
|
104
|
+
|
105
|
+
With this case, now #dash_x and #exclusive give the same result when applied to the usage
|
106
|
+
variable.
|
107
|
+
|
108
|
+
6. Typed options
|
109
|
+
|
110
|
+
Starting with version 0.3, typed options are handled by a plugin architecture. There
|
111
|
+
are also more types supported than pre 0.3. Typed options are invoked by preceeding an
|
112
|
+
argument with a string of non-alpha chars.
|
113
|
+
|
114
|
+
The following are built into the library (some I took from BASIC which I programmed in
|
115
|
+
long, long ago). Note: These can be replaced or added to.
|
116
|
+
|
117
|
+
% - Integer
|
118
|
+
$ - String (but this is unnecessary as this is default)
|
119
|
+
# - Float
|
120
|
+
@ - Date-Time
|
121
|
+
< - A file that will be read
|
122
|
+
> - A file that will be written to
|
123
|
+
<< - A file that will be read in by readlines
|
124
|
+
>> - A file that will be appended to
|
125
|
+
>? - A file that will be written to but the user is prompted if it already exists
|
126
|
+
>>? - A file that will be appended to but the user is prompted if the file doesn't exist
|
127
|
+
|
128
|
+
So when you send the argument message to the usage object, you will get a value of that
|
129
|
+
type and if the user does not give that type, then they get an error message.
|
130
|
+
|
131
|
+
usage = Usage.new "%num_times @on_date"
|
132
|
+
|
133
|
+
In this example, #num_times returns and Integer object and #on_date returns a Time object.
|
134
|
+
|
135
|
+
6.1. Adding new types
|
136
|
+
|
137
|
+
You can add a new type parser by declaring a sub-class of UsageMod::ArgumentParserPlugin or
|
138
|
+
one of the other sub-classes of Usage::ArgumentParserPlugin such as UsageMod::IntegerArgumentPlugin.
|
139
|
+
|
140
|
+
You need to define two methods:
|
141
|
+
|
142
|
+
initialize(usage_ui, string)
|
143
|
+
- usage_ui is used to ask the user yes/no questions
|
144
|
+
- string is the argument as a string. If the parsing fails, then raise an exception
|
145
|
+
that is a sub-class of UsageMod::Error
|
146
|
+
|
147
|
+
close
|
148
|
+
- use this to close any objects that need to be closed/released when the usage block ends
|
149
|
+
|
150
|
+
6.2 Assigning them to a string of chars
|
151
|
+
|
152
|
+
To assign a plugin class to a character, you only need to call UsageMod::Base.add_type_handler.
|
153
|
+
|
154
|
+
So if the class that you created is called SpanishArgumentPlugin, and you would like to
|
155
|
+
assign it to the '^^' character sequence, then you would call:
|
156
|
+
|
157
|
+
UsageMod::Base.add_type_handler("^^", SpanishArgumentPlugin)
|
158
|
+
|
159
|
+
6.3 Example
|
160
|
+
|
161
|
+
#
|
162
|
+
# This example is taken from the built-in class for arguments that are writable files
|
163
|
+
# that don't want to be overwritten
|
164
|
+
#
|
165
|
+
|
166
|
+
# first define the file exists exception class
|
167
|
+
class FileOutputExistsError < UsageMod::Error
|
168
|
+
attr_reader :filename
|
169
|
+
def initialize(filename)
|
170
|
+
@filename = filename
|
171
|
+
super("output file exists: '#{filename}'")
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# next define the argument parser plugin
|
176
|
+
class FileOutputQueryPlugin < UsageMod::ArgumentParserPlugin
|
177
|
+
def initialize(usage_ui, str)
|
178
|
+
if FileTest.exist?(str) then
|
179
|
+
raise FileOutputExistsError.new(str) if usage_ui.ask_yes_no(OVERWRITE_QUERY % str, NO_RESPONSE) == NO_RESPONSE
|
180
|
+
end
|
181
|
+
@value = File.open(str, "w")
|
182
|
+
end
|
183
|
+
|
184
|
+
def close
|
185
|
+
@value.close
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# lastly attach that parser to the character sequence '>?'
|
190
|
+
UsageMod::Base.add_type_handler(">?", FileOutputQueryPlugin)
|
191
|
+
|
192
|
+
|
193
|
+
7. Choice options
|
194
|
+
|
195
|
+
You can have optional options that have a set of values which they can be. The choices
|
196
|
+
are separated by pipe symbols. See below:
|
197
|
+
|
198
|
+
usage = Usage.new "[-a coffee|tea|milk]"
|
199
|
+
|
200
|
+
After this #dash_a will give the string coffee, tea, or milk. If the value given isn't
|
201
|
+
one of the given choices, then the user is given an error message with the
|
202
|
+
appropriate choices.
|
203
|
+
|
204
|
+
8. Usage blocks
|
205
|
+
|
206
|
+
Starting with version 0.3, you can run usage within a block. This allows any objects that need
|
207
|
+
clean-up when the block exits (such as open files). To do this it looks like the following:
|
208
|
+
|
209
|
+
Usage.new "<infile >outfile" do |usage|
|
210
|
+
usage.outfile.write(usage.infile.read)
|
211
|
+
end # here the file objects are closed
|
212
|
+
|
213
|
+
If you do it without a block, then you would have to do the following:
|
214
|
+
|
215
|
+
usage = Usage.new "<infile >outfile"
|
216
|
+
usage.outfile.write(usage.infile.read)
|
217
|
+
usage.infile.close
|
218
|
+
usage.outfile.close
|
219
|
+
|