usage 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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
+
data/TODO ADDED
@@ -0,0 +1,2 @@
1
+ 1. Parse more styles of date/time than currently
2
+