rant 0.3.2 → 0.3.4
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/NEWS +22 -1
- data/README +9 -12
- data/Rantfile +32 -9
- data/devel-notes +16 -0
- data/doc/advanced.rdoc +55 -0
- data/doc/rant.rdoc +27 -2
- data/doc/rantfile.rdoc +104 -30
- data/install.rb +6 -0
- data/lib/rant.rb +5 -1
- data/lib/rant/import.rb +12 -4
- data/lib/rant/import/rubydoc.rb +1 -1
- data/lib/rant/import/rubypackage.rb +2 -2
- data/lib/rant/import/rubytest.rb +3 -2
- data/lib/rant/plugin/csharp.rb +5 -9
- data/lib/rant/rantenv.rb +1 -0
- data/lib/rant/rantfile.rb +184 -68
- data/lib/rant/rantlib.rb +203 -195
- data/lib/rant/rantsys.rb +87 -44
- data/lib/rant/rantvar.rb +236 -17
- data/rantmethods.rb +9 -4
- data/run_rant +1 -1
- data/test/Rantfile +16 -0
- data/test/plugin/csharp/test_csharp.rb +2 -1
- data/test/plugin/rantfile +1 -0
- data/test/project1/Rantfile +8 -4
- data/test/rule.rf +27 -0
- data/test/subdirs/Rantfile +2 -0
- data/test/subdirs/sub2/sub/rantfile +17 -0
- data/test/subdirs/test_subdirs.rb +46 -2
- data/test/test_dirtask.rb +33 -0
- data/test/test_filelist.rb +81 -0
- data/test/test_filetask.rb +6 -6
- data/test/test_rant_interface.rb +4 -3
- data/test/test_rule.rb +67 -0
- data/test/test_source.rb +26 -0
- data/test/test_task.rb +27 -6
- data/test/test_var.rb +102 -0
- data/test/toplevel.rf +1 -1
- data/test/var.rf +19 -0
- metadata +11 -2
data/NEWS
CHANGED
@@ -1,6 +1,27 @@
|
|
1
1
|
|
2
2
|
= Rant NEWS
|
3
3
|
|
4
|
+
== Rant 0.3.4
|
5
|
+
|
6
|
+
Incompatible changes:
|
7
|
+
* Arguments of the form VAR=VAL to rant no longer set environment
|
8
|
+
variables directly, they are available through +var+ now. Read
|
9
|
+
doc/advanced.rdoc[link:files/doc/advanced_rdoc.html] for more info.
|
10
|
+
* Replace any
|
11
|
+
include Sys
|
12
|
+
with
|
13
|
+
include Rant::Sys
|
14
|
+
in Rantfiles. Or even better: don't include the +Sys+ module.
|
15
|
+
|
16
|
+
New features:
|
17
|
+
* Installation with install.rb installs .cmd files on Windows.
|
18
|
+
Read README[link:files/README.html]
|
19
|
+
* Sharing variables between Rantfiles. Read
|
20
|
+
doc/advanced.rdoc[link:files/doc/advanced_rdoc.html] for more info.
|
21
|
+
* Selecting files with the +sys+ command.
|
22
|
+
* Rules
|
23
|
+
Read doc/rantfile.rdoc[link:files/doc/rantfile_rdoc.html] for docu.
|
24
|
+
|
4
25
|
== Rant 0.3.2
|
5
26
|
|
6
27
|
This version should be fully backwards compatible to 0.3.0.
|
@@ -9,7 +30,7 @@ New features:
|
|
9
30
|
* Support splitting your buildfiles up and placing them into multiple
|
10
31
|
directories with the +subdirs+ command. Please read
|
11
32
|
doc/rantfile.rdoc[link:files/doc/rantfile_rdoc.html] for usage.
|
12
|
-
This is especially
|
33
|
+
This is especially useful for bigger projects.
|
13
34
|
|
14
35
|
== Rant 0.3.0
|
15
36
|
|
data/README
CHANGED
@@ -36,10 +36,10 @@ Running rant in the directory of this file:
|
|
36
36
|
will ensure that the "data" file in the "backup" directory is up to
|
37
37
|
date.
|
38
38
|
|
39
|
-
This document was written for version 0.3.
|
39
|
+
This document was written for version 0.3.4 of Rant. Most things
|
40
40
|
described here will work for older/newer versions of Rant, but look at
|
41
41
|
the README file in the Rant distribution you've installed for exact
|
42
|
-
documentation
|
42
|
+
documentation of your Rant version.
|
43
43
|
|
44
44
|
== Support
|
45
45
|
|
@@ -103,18 +103,14 @@ is done, you have successfully installed Rant. Congratulations!
|
|
103
103
|
First download the latest version of Rant from
|
104
104
|
http://rubyforge.org/frs/?group_id=615. Choose the .zip or .tar.gz
|
105
105
|
file, whatever you like, with the highest version number. Then unpack
|
106
|
-
the archive, cd to the new directory and run the
|
107
|
-
could look like:
|
106
|
+
the archive, cd to the new directory and run the install.rb script.
|
107
|
+
This could look like:
|
108
108
|
% tar -xzf rant-<version>.tar.gz
|
109
109
|
% cd rant-<version>
|
110
|
-
% ruby
|
110
|
+
% ruby install.rb
|
111
111
|
Depending on your Ruby installation, you'll probably need superuser
|
112
|
-
privileges for the last command.
|
113
|
-
|
114
|
-
% ruby setup.rb --help
|
115
|
-
which will show you a bunch of options for the installation.
|
116
|
-
|
117
|
-
After performing the steps listed above, try to run
|
112
|
+
privileges for the last command.
|
113
|
+
Finally try to run
|
118
114
|
% rant --version
|
119
115
|
to verify Rant was installed correctly.
|
120
116
|
|
@@ -202,7 +198,8 @@ Rant was tested on:
|
|
202
198
|
Linux 1.8.2
|
203
199
|
1.9
|
204
200
|
MacOS X 1.8.2
|
205
|
-
|
201
|
+
Windows XP 1.8.2 (OneClick Installer)
|
202
|
+
Windows 2000 1.8.2 (OneClick Installer)
|
206
203
|
|
207
204
|
It *should* run on most platforms where Ruby runs, but you never
|
208
205
|
know...
|
data/Rantfile
CHANGED
@@ -5,9 +5,10 @@ import %w(rubytest rubydoc rubypackage)
|
|
5
5
|
|
6
6
|
task :default => :test
|
7
7
|
|
8
|
-
lib_files =
|
9
|
-
dist_files =
|
10
|
-
|
8
|
+
lib_files = sys["lib/**/*.rb"]
|
9
|
+
dist_files = sys["{bin,lib,test,doc}/**/*"].shun("html", "coverage") +
|
10
|
+
sys["*"].no_dir.exclude("InstalledFiles", "Session.vim")
|
11
|
+
bin_files = sys["bin/*"]
|
11
12
|
|
12
13
|
gen RubyPackage, "rant" do |t|
|
13
14
|
t.version = `#{Env::RUBY} run_rant --version`.split[1]
|
@@ -18,7 +19,7 @@ gen RubyPackage, "rant" do |t|
|
|
18
19
|
t.author = "Stefan Lang"
|
19
20
|
t.email = "langstefan@gmx.at"
|
20
21
|
t.rubyforge_project = "make"
|
21
|
-
t.gem_extra_rdoc_files =
|
22
|
+
t.gem_extra_rdoc_files = sys["**/README", "NEWS"].no_dir("pkg").no_dir("test") + sys["doc/**/*.rdoc"]
|
22
23
|
t.homepage = "http://make.rubyforge.org"
|
23
24
|
desc "Create packages for distribution."
|
24
25
|
t.package_task
|
@@ -59,7 +60,7 @@ end
|
|
59
60
|
desc "Test plugins."
|
60
61
|
gen RubyTest, :testplugins do |g|
|
61
62
|
g.libs << "test"
|
62
|
-
g.test_files =
|
63
|
+
g.test_files = sys["test/plugin/**/test_*"]
|
63
64
|
end
|
64
65
|
|
65
66
|
desc "Run all tests and generate coverage with rcov."
|
@@ -73,13 +74,13 @@ end
|
|
73
74
|
desc "Test project with subdirs."
|
74
75
|
gen RubyTest, :tsubdirs do |t|
|
75
76
|
t.libs << "test"
|
76
|
-
t.test_files =
|
77
|
+
t.test_files = sys["test/subdirs/test_*.rb"]
|
77
78
|
end
|
78
79
|
|
79
80
|
desc "Run all tests."
|
80
81
|
gen RubyTest, :tall do |g|
|
81
82
|
g.libs << "test"
|
82
|
-
g.test_files =
|
83
|
+
g.test_files = sys["test/**/test_*.rb"]
|
83
84
|
end
|
84
85
|
task :testall => %w(test testp1 testp2 testrb1 testplugins)
|
85
86
|
|
@@ -87,6 +88,7 @@ desc "Remove autogenerated files."
|
|
87
88
|
task :clean do
|
88
89
|
sys.rm_f %w(InstalledFiles .config bench-rant bench-depsearch)
|
89
90
|
sys.rm_rf %w(doc/html pkg test/coverage)
|
91
|
+
sys.rm_f sys["bin/*.cmd"]
|
90
92
|
end
|
91
93
|
|
92
94
|
desc "Publish html docs on make.rubyfore.org.",
|
@@ -104,8 +106,29 @@ task "to-win" => :package do
|
|
104
106
|
win_dir = "/mnt/data_fat/stefan/Ruby"
|
105
107
|
Dir["pkg/*"].each { |f|
|
106
108
|
target = File.join(win_dir, File.basename(f))
|
107
|
-
|
108
|
-
|
109
|
+
(file target => f do |t|
|
110
|
+
sys.rm_rf target if test(?e, target)
|
111
|
+
sys.cp_r f, t.name
|
112
|
+
end).invoke
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
116
|
+
desc "Install Rant."
|
117
|
+
install = task :install do
|
118
|
+
sys.ruby "setup.rb"
|
119
|
+
end
|
120
|
+
|
121
|
+
if Env.on_windows?
|
122
|
+
cmd_files = bin_files.find_all { |f| f !~ /\.cmd$/}
|
123
|
+
cmd_files.map! { |f| "#{f}.cmd" }
|
124
|
+
cmd_files.zip(bin_files).each { |cmd, bin|
|
125
|
+
file cmd do |t|
|
126
|
+
File.open(t.name, "w") { |f|
|
127
|
+
i_bin = File.join(Env::RUBY_BINDIR, File.basename(bin))
|
128
|
+
f.puts "@#{sys.sp Env::RUBY} #{sys.sp i_bin} %*"
|
129
|
+
}
|
130
|
+
end
|
131
|
+
install << cmd
|
109
132
|
}
|
110
133
|
end
|
111
134
|
|
data/devel-notes
CHANGED
@@ -63,4 +63,20 @@ following:
|
|
63
63
|
* Worker#needed?
|
64
64
|
* RantApp#run before returning.
|
65
65
|
|
66
|
+
== RantContext#rantapp rename
|
67
|
+
Rename +rantapp+ to +rac+.
|
68
|
+
Done.
|
69
|
+
|
70
|
+
== Circular dependencies
|
71
|
+
Before Rant detected circular dependencies, we silently removed a task
|
72
|
+
dependency on itself. Should we remove this mechanism now?
|
73
|
+
|
74
|
+
== Testing
|
75
|
+
Files/directories created during tests match the glob *.t*
|
76
|
+
so don't give names matching this pattern to regular files, as they
|
77
|
+
would soon be deleted!
|
78
|
+
|
79
|
+
== Compiling C
|
80
|
+
make uses the env. variables CC and CFLAGS.
|
81
|
+
|
66
82
|
# vim:tw=70:
|
data/doc/advanced.rdoc
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
|
2
|
+
== Advanced Rantfiles
|
3
|
+
|
4
|
+
=== Sharing variables
|
5
|
+
|
6
|
+
The +var+ command allows you to share variables between Rantfiles and
|
7
|
+
to set variables from the commandline. Just use +var+ like a hash to
|
8
|
+
set/get variables:
|
9
|
+
var[:manifest] = %w(README Rantfile myprog.rb lib)
|
10
|
+
In this example, :manifest is the variable name, which has to be a
|
11
|
+
string or symbol. Symbols as variable names are converted to strings.
|
12
|
+
Now you can access the "manifest" variable in every Rantfile of your
|
13
|
+
project:
|
14
|
+
file "MANIFEST" do |t|
|
15
|
+
open(t.name, "w") { |f|
|
16
|
+
var[:manifest].each { |str| f.puts(str) }
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
Arguments of the form VAR=VAL to the rant command are also available
|
21
|
+
through +var+:
|
22
|
+
% cat Rantfile
|
23
|
+
task :show_test do
|
24
|
+
puts var[:test]
|
25
|
+
end
|
26
|
+
% rant
|
27
|
+
nil
|
28
|
+
% rant test=hello
|
29
|
+
hello
|
30
|
+
|
31
|
+
For some variables it is necessary to make them available for
|
32
|
+
subprocesses, like CC or CFLAGS:
|
33
|
+
var.env %w(CC CFLAGS)
|
34
|
+
The variables CC and CFLAGS are available through +var+ as always and
|
35
|
+
are mapped to environment variables.
|
36
|
+
|
37
|
+
=== The "ignore" variable
|
38
|
+
|
39
|
+
The "ignore" variable is a list of files/regular expressions which are
|
40
|
+
ignored when selecting files with the +sys+ command:
|
41
|
+
var[:ignore] = ["CVS", /~$/]
|
42
|
+
If you select files with <tt>sys[]</tt> now, the lists won't contain
|
43
|
+
any directory/file named "CVS" or ending in ~. Examples would be:
|
44
|
+
CVS
|
45
|
+
src/CVS
|
46
|
+
src/CVS/xy
|
47
|
+
xy~
|
48
|
+
src/util.c~
|
49
|
+
|
50
|
+
== See also
|
51
|
+
|
52
|
+
Rantfile basics::
|
53
|
+
doc/rantfile.rdoc[link:files/doc/rantfile_rdoc.html]
|
54
|
+
Rant Overview::
|
55
|
+
README[link:files/README.html]
|
data/doc/rant.rdoc
CHANGED
@@ -5,13 +5,31 @@ For a short usage message and a list of options invoke rant with the
|
|
5
5
|
<tt>--help</tt> option:
|
6
6
|
% rant --help
|
7
7
|
|
8
|
+
Usually you'll run rant by giving it the name of the task(s) to be
|
9
|
+
invoked as argument(s).
|
10
|
+
To get an overview for the project in the current working directory,
|
11
|
+
run rant with the <tt>--tasks</tt> (short form: <tt>-T</tt>) option:
|
12
|
+
% rant -T
|
13
|
+
rant # => test
|
14
|
+
rant package # Create packages for distribution.
|
15
|
+
rant doc # Generate documentation.
|
16
|
+
rant test # Run unit tests.
|
17
|
+
rant cov # Run all tests and generate coverage with rcov.
|
18
|
+
rant clean # Remove autogenerated files.
|
19
|
+
rant publish-docs # Publish html docs on RubyForge.
|
20
|
+
# Note: scp will prompt for rubyforge password.
|
21
|
+
This lists the "public" tasks for the project. The first line always
|
22
|
+
tells you the task(s) that will be invoked when no argument is given
|
23
|
+
to rant, in the above example, this would be the +test+ task.
|
24
|
+
|
8
25
|
When you invoke rant on the commandline it performs the following
|
9
26
|
steps (roughly):
|
10
27
|
|
11
28
|
1. Process commandline options and arguments.
|
12
29
|
An option starts with two dashes or one for the single letter
|
13
|
-
equivalent. Arguments of the form <tt>VAR=VAL</tt> set
|
14
|
-
|
30
|
+
equivalent. Arguments of the form <tt>VAR=VAL</tt> set variables
|
31
|
+
available in the Rantfile(s). All other arguments are names of
|
32
|
+
tasks to be invoked.
|
15
33
|
2. Load Rantfiles in working directory. Rantfiles with the following
|
16
34
|
names are recognized:
|
17
35
|
Rantfile
|
@@ -22,3 +40,10 @@ steps (roughly):
|
|
22
40
|
was given on the commandline, a task called "default" will be
|
23
41
|
invoked. If the "default" task doesn't exist, the first task will
|
24
42
|
be invoked.
|
43
|
+
|
44
|
+
== See also
|
45
|
+
|
46
|
+
Rant Overview::
|
47
|
+
README[link:files/README.html]
|
48
|
+
Independent from Rant? The <tt>rant-import</tt> command::
|
49
|
+
doc/rant-import.rdoc[link:files/doc/rant-import_rdoc.html]
|
data/doc/rantfile.rdoc
CHANGED
@@ -47,7 +47,7 @@ anything. To associate an action with the task, add a block:
|
|
47
47
|
Put these 3 lines of code into a file called +Rantfile+ and run rant:
|
48
48
|
% rant mytask
|
49
49
|
Hello, mytask running.
|
50
|
-
Note: you could have
|
50
|
+
Note: you could have omited the mytask argument to rant because it is
|
51
51
|
the only task in the Rantfile.
|
52
52
|
|
53
53
|
You can add a block parameter which will be a reference to the created
|
@@ -149,36 +149,70 @@ Only the tasks which have a description are listed.
|
|
149
149
|
=== The +sys+ function
|
150
150
|
|
151
151
|
After using the +sys+ function quite often in the examples, I should
|
152
|
-
explain it a little bit. The +sys+ function can be used in
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
152
|
+
explain it a little bit. The +sys+ function can be used in three ways:
|
153
|
+
|
154
|
+
1. <b>File system operations</b>
|
155
|
+
|
156
|
+
The first form is with no arguments. It returns an object on which you
|
157
|
+
can invoke the methods of the +FileUtils+ module that comes with ruby.
|
158
|
+
Examples are:
|
159
|
+
sys.rm "file1", "file2", ... # remove files
|
160
|
+
sys.cp "src", "dest" # copy from "src" do "dest"
|
161
|
+
sys.mkdir "dir" # create directory "dir"
|
162
|
+
For a list of all available methods invoke ri:
|
163
|
+
% ri FileUtils
|
164
|
+
which will also show documentation for them.
|
165
|
+
Additionally you have the following methos which are not in the
|
166
|
+
FileUtils module:
|
167
|
+
sys.ruby "arg1", "arg2", ... # invoke the ruby interpreter
|
168
|
+
sys.safe_ln "src", "dest" # create a hardlink or fall back to
|
169
|
+
# copying
|
170
|
+
If you're tired of typing <tt>sys.</tt> all the time, you can include
|
171
|
+
the Rant::Sys module in your Rantfile (though it is not recommended
|
172
|
+
to do this):
|
173
|
+
include Rant::Sys
|
174
|
+
task :clean do
|
175
|
+
rm_f "myprog"
|
176
|
+
end
|
175
177
|
Then you can invoke these methods without the <tt>sys</tt> function.
|
176
|
-
Note:: You are highly encouraged to not <tt>include Sys</tt>
|
177
|
-
your Rantfile as this introduces many methods in the
|
178
|
-
namespace, which can cause name clashes with other
|
179
|
-
Of course if you have just some tasks to remove
|
180
|
-
in your home directory and do not use other Ruby
|
181
|
-
your Rantfile, it is no problem.
|
178
|
+
Note:: You are highly encouraged to not <tt>include Rant::Sys</tt>
|
179
|
+
into your Rantfile as this introduces many methods in the
|
180
|
+
global namespace, which can cause name clashes with other
|
181
|
+
libraries. Of course if you have just some tasks to remove
|
182
|
+
backup files in your home directory and do not use other Ruby
|
183
|
+
libraries in your Rantfile, it is no problem.
|
184
|
+
|
185
|
+
2. <b>Running external commands</b>
|
186
|
+
|
187
|
+
Invoke the +sys+ function with a string as argument to run a shell:
|
188
|
+
sys "echo *.c"
|
189
|
+
will print a list of C files to stdout.
|
190
|
+
|
191
|
+
When given multiple arguments, +sys+ invokes the program named with
|
192
|
+
the first argument giving it the remaining arguments as arguments:
|
193
|
+
sys "echo", "*.c"
|
194
|
+
will print "*.c" to stdout.
|
195
|
+
|
196
|
+
3. <b>Selecting files</b>
|
197
|
+
|
198
|
+
To select files with the help of glob patterns use +sys+ with the
|
199
|
+
<tt>[]</tt> operator:
|
200
|
+
file "program" => sys["*.o"]
|
201
|
+
The task "program" depends on all files ending in ".o". Rant uses
|
202
|
+
the Dir::glob method internally to resolve patterns, so you can
|
203
|
+
read the ri docs to get an overview:
|
204
|
+
ri Dir::glob
|
205
|
+
|
206
|
+
You can give more patterns:
|
207
|
+
c_files = sys["**/*.h", "**/*.c"]
|
208
|
+
gives a list of all files ending in ".h" or ".c" in the current
|
209
|
+
directory and all subdirectories.
|
210
|
+
|
211
|
+
The object returned by sys#[] _behaves_ like a list of string, so
|
212
|
+
it is possible to pass it to methods expecting an array. If you're
|
213
|
+
getting errors or experience strange behaviour convert the list
|
214
|
+
explicetely to an array:
|
215
|
+
sys.touch c_files.to_a
|
182
216
|
|
183
217
|
=== Generators
|
184
218
|
|
@@ -218,6 +252,44 @@ The +act+ block of the "install" task will only be run if
|
|
218
252
|
"InstalledFiles" doesn't exist. Of course you can add prerequisites
|
219
253
|
like with any other task.
|
220
254
|
|
255
|
+
=== Rules
|
256
|
+
|
257
|
+
A Rule allows you to tell Rant how it should build files matching a
|
258
|
+
common pattern, e.g. how to build files ending in ".o". A standard
|
259
|
+
rule usage is to create C object files:
|
260
|
+
gen Rule, '.o' => '.c' do |t|
|
261
|
+
sys "cc -c -o #{t.name} #{t.source}"
|
262
|
+
end
|
263
|
+
Assuming that we have the C source file util.c in the current
|
264
|
+
directory:
|
265
|
+
% rant util.o
|
266
|
+
cc -c -o util.o util.c
|
267
|
+
Because Rant didn't find a task for util.o, it looked for a matching
|
268
|
+
rule and created a task for util.o.
|
269
|
+
|
270
|
+
The first line above could also be written as:
|
271
|
+
gen Rule, :o => :c do |t|
|
272
|
+
|
273
|
+
The +source+ method of the task object gives us the first dependency.
|
274
|
+
So the following line has the same effect:
|
275
|
+
sys "cc -c -o #{t.name} #{t.prerequisites.first}"
|
276
|
+
|
277
|
+
You can also refine the rule pattern by using a regular expression. To
|
278
|
+
refine dependency selection give a block as source argument:
|
279
|
+
src = lambda { |target| [target.sub_ext("c"), target.sub_ext("h")] }
|
280
|
+
gen Rule, /^my_[^.]+\.o$/ => src do |t|
|
281
|
+
sys "cc -c -o #{t.name} #{t.source}"
|
282
|
+
end
|
283
|
+
This rule generates a task for files beginning with "my_" and ending
|
284
|
+
in ".o" (like "my_program.o"). The task has a file ending in ".c" and
|
285
|
+
one ending in ".h" as dependencies (like "my_program.c" and
|
286
|
+
"my_program.h") . Since <tt>t.source</tt> gives us the *first*
|
287
|
+
dependency, the ".c" file will appear as argument to cc, but not the
|
288
|
+
".h" file.
|
289
|
+
|
290
|
+
The +sub_ext+ method of the +String+ class replaces anything after the
|
291
|
+
last dot with the given string.
|
292
|
+
|
221
293
|
=== Importing additional generators
|
222
294
|
|
223
295
|
The +import+ function lets you import additional generators.
|
@@ -326,6 +398,8 @@ because it refers to a main Rantfile.
|
|
326
398
|
|
327
399
|
== See also
|
328
400
|
|
401
|
+
Advanced Rantfiles::
|
402
|
+
doc/advanced.rdoc[link:files/doc/advanced_rdoc.html]
|
329
403
|
Rant Overview::
|
330
404
|
README[link:files/README.html]
|
331
405
|
Ruby project howto::
|