usage 0.0.3

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.
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
+