reacco 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Gemfile +2 -0
- data/HISTORY.md +4 -0
- data/README.md +26 -0
- data/Rakefile +31 -0
- data/bin/reacco +95 -0
- data/data/index.erb +17 -0
- data/data/style.css +341 -0
- data/lib/reacco/extractor/block.rb +84 -0
- data/lib/reacco/extractor.rb +96 -0
- data/lib/reacco/filters/brief.rb +12 -0
- data/lib/reacco/filters/headingid.rb +13 -0
- data/lib/reacco/filters/hgroup.rb +26 -0
- data/lib/reacco/filters/literate.rb +36 -0
- data/lib/reacco/filters/prelang.rb +11 -0
- data/lib/reacco/filters/sections.rb +56 -0
- data/lib/reacco/filters/toc.rb +54 -0
- data/lib/reacco/generator.rb +68 -0
- data/lib/reacco/readme.rb +150 -0
- data/lib/reacco/version.rb +5 -0
- data/lib/reacco.rb +44 -0
- data/reacco.gemspec +17 -0
- data/test/blocks_test.rb +18 -0
- data/test/test_helper.rb +9 -0
- metadata +103 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
/doc/
|
data/Gemfile
ADDED
data/HISTORY.md
ADDED
data/README.md
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# Reacco
|
2
|
+
#### Readme documentation prettifier
|
3
|
+
|
4
|
+
In progress. Check out http://ricostacruz.com/sinatra-assetpack for an example.
|
5
|
+
|
6
|
+
# API reference
|
7
|
+
|
8
|
+
(API reference goes here)
|
9
|
+
|
10
|
+
Acknowledgements
|
11
|
+
----------------
|
12
|
+
|
13
|
+
© 2011, Rico Sta. Cruz. Released under the [MIT
|
14
|
+
License](http://www.opensource.org/licenses/mit-license.php).
|
15
|
+
|
16
|
+
Reacco is authored and maintained by [Rico Sta. Cruz][rsc] with help from it's
|
17
|
+
[contributors][c]. It is sponsored by my startup, [Sinefunc, Inc][sf].
|
18
|
+
|
19
|
+
* [My website](http://ricostacruz.com) (ricostacruz.com)
|
20
|
+
* [Sinefunc, Inc.](http://sinefunc.com) (sinefunc.com)
|
21
|
+
* [Github](http://github.com/rstacruz) (@rstacruz)
|
22
|
+
* [Twitter](http://twitter.com/rstacruz) (@rstacruz)
|
23
|
+
|
24
|
+
[rsc]: http://ricostacruz.com
|
25
|
+
[c]: http://github.com/rstacruz/reacco/contributors
|
26
|
+
[sf]: http://sinefunc.com
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
desc "Invokes the test suite in multiple RVM environments"
|
2
|
+
task :'test!' do
|
3
|
+
# Override this by adding RVM_TEST_ENVS=".." in .rvmrc
|
4
|
+
envs = ENV['RVM_TEST_ENVS'] || '1.9.2@reacco,,1.8.7@reacco'
|
5
|
+
puts "* Testing in the following RVM environments: #{envs.gsub(',', ', ')}"
|
6
|
+
system "rvm #{envs} rake test" or abort
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "Runs tests"
|
10
|
+
task :test do
|
11
|
+
Dir['test/*_test.rb'].each { |f| load f }
|
12
|
+
end
|
13
|
+
|
14
|
+
task :default => :test
|
15
|
+
|
16
|
+
gh = "rstacruz/reacco"
|
17
|
+
namespace :doc do
|
18
|
+
# http://github.com/rstacruz/reacco
|
19
|
+
desc "Builds the documentation into doc/"
|
20
|
+
task :build do
|
21
|
+
system "reacco -a --github #{gh} --api lib"
|
22
|
+
end
|
23
|
+
|
24
|
+
# http://github.com/rstacruz/git-update-ghpages
|
25
|
+
desc "Posts documentation to GitHub pages"
|
26
|
+
task :deploy => :build do
|
27
|
+
system "git update-ghpages #{gh} -i doc/"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
task :doc => :'doc:build'
|
data/bin/reacco
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$:.unshift File.expand_path('../../lib', __FILE__)
|
3
|
+
require 'reacco'
|
4
|
+
|
5
|
+
module Params
|
6
|
+
def extract(what) i = index(what) and slice!(i, 2)[1] end;
|
7
|
+
end
|
8
|
+
ARGV.extend Params
|
9
|
+
|
10
|
+
def tip(str)
|
11
|
+
$stderr.write "#{str}\n"
|
12
|
+
end
|
13
|
+
|
14
|
+
if ARGV == ['-h'] || ARGV == ['--help']
|
15
|
+
tip "Usage: reacco [options]"
|
16
|
+
tip "Generates nice HTML."
|
17
|
+
tip ""
|
18
|
+
tip "Options:"
|
19
|
+
tip " --literate Move <pre> blocks to the right."
|
20
|
+
tip " --toc Show a table of contents."
|
21
|
+
tip ""
|
22
|
+
tip "Theming:"
|
23
|
+
tip " --css FILE Append the CSS from that file to the generated CSS."
|
24
|
+
tip " -t, --template PATH The template to use, if you don't want the default."
|
25
|
+
tip ""
|
26
|
+
tip "API extraction:"
|
27
|
+
tip " --api PATH Where to get stuff."
|
28
|
+
tip ""
|
29
|
+
tip "More:"
|
30
|
+
tip " --github REPO Adds a 'Fork me on GitHub' badge. Repo should be in"
|
31
|
+
tip " 'username/reponame' format."
|
32
|
+
tip " -o, --output PATH Sets the path to put HTML files in. Defaults to 'doc/'."
|
33
|
+
tip ""
|
34
|
+
exit
|
35
|
+
|
36
|
+
else
|
37
|
+
# Build options from arguments.
|
38
|
+
switches = [:literate, :toc]
|
39
|
+
docpath = ARGV.extract('-o') || ARGV.extract('--output') || 'doc'
|
40
|
+
template = ARGV.extract('-t') || ARGV.extract('--template') || nil
|
41
|
+
github = ARGV.extract('--github') || nil
|
42
|
+
css = ARGV.extract('--css') || nil
|
43
|
+
awesome = ARGV.delete('-a') || ARGV.extract('--awesome')
|
44
|
+
options = Hash.new
|
45
|
+
|
46
|
+
extract = Array.new
|
47
|
+
while (x = ARGV.extract('--api'))
|
48
|
+
extract << x
|
49
|
+
end
|
50
|
+
|
51
|
+
switches.each do |opt|
|
52
|
+
options[opt] = true if ARGV.delete("--#{opt}")
|
53
|
+
end
|
54
|
+
|
55
|
+
switches.each { |opt| options[opt] = true } if awesome
|
56
|
+
|
57
|
+
options[:github] = github if github
|
58
|
+
|
59
|
+
if ARGV.any?
|
60
|
+
puts "Invalid usage. See `reacco --help` for help."
|
61
|
+
exit 60
|
62
|
+
end
|
63
|
+
|
64
|
+
# Lets find the readme.
|
65
|
+
readme = Reacco::Readme.new(options)
|
66
|
+
unless readme.exists?
|
67
|
+
tip "Readme file not found."
|
68
|
+
exit 60
|
69
|
+
end
|
70
|
+
|
71
|
+
# If we need to extract API, let's do it.
|
72
|
+
if extract.any?
|
73
|
+
blocks = extract.map { |path|
|
74
|
+
Reacco::Extractor.new(Dir["#{path}/**/*"]).blocks
|
75
|
+
}.flatten
|
76
|
+
|
77
|
+
blocks.each { |blk| readme.inject_api_block blk.to_html }
|
78
|
+
end
|
79
|
+
|
80
|
+
# Now let's generate it.
|
81
|
+
gen = Reacco::Generator.new(readme, docpath, template, css)
|
82
|
+
unless gen.template?
|
83
|
+
tip "Invalid template."
|
84
|
+
tip "A template must at least have an index.xxx page."
|
85
|
+
exit 61
|
86
|
+
end
|
87
|
+
|
88
|
+
gen.write! { |file| tip "* #{file}" }
|
89
|
+
|
90
|
+
# AHHH YYEAAAHHH!
|
91
|
+
tip "Construction complete."
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
#tip Reacco.html(:hgroup => true, :literate => true, :brief => true)
|
data/data/index.erb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8" />
|
5
|
+
<title><%= title %></title>
|
6
|
+
<link href="style.css" rel="stylesheet" />
|
7
|
+
<link href='http://fonts.googleapis.com/css?family=Shanti:400|PT+Sans:700,700italic' rel='stylesheet' type='text/css'>
|
8
|
+
</head>
|
9
|
+
<body class='<%= body_class %>'>
|
10
|
+
<% if github %>
|
11
|
+
<a href="<%= github %>"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://a248.e.akamai.net/assets.github.com/img/e6bef7a091f5f3138b8cd40bc3e114258dd68ddf/687474703a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f7265645f6161303030302e706e67" alt="Fork me on GitHub"></a>
|
12
|
+
<% end %>
|
13
|
+
<div id='all'><%= yield %></div>
|
14
|
+
<script type='text/javascript' src='http://cachedcommons.org/cache/prettify/1.0.0/javascripts/prettify-min.js'></script>
|
15
|
+
<script>prettyPrint();</script>
|
16
|
+
</body>
|
17
|
+
</html>
|
data/data/style.css
ADDED
@@ -0,0 +1,341 @@
|
|
1
|
+
html, body {
|
2
|
+
margin: 0;
|
3
|
+
padding: 0; }
|
4
|
+
|
5
|
+
body {
|
6
|
+
background: #fcfcfa;
|
7
|
+
font-family: 'pt sans', sans-serif;
|
8
|
+
color: #444;
|
9
|
+
font-size: 11pt;
|
10
|
+
line-height: 1.5; }
|
11
|
+
|
12
|
+
::-moz-selection { background: #88a; color: #fff; text-shadow: none; }
|
13
|
+
::selection { background: #88a; color: #fff; text-shadow: none; }
|
14
|
+
|
15
|
+
h1, h2, h3, h4, h5, h6, p, ol, ul, pre, ol>li {
|
16
|
+
max-width: 500px;
|
17
|
+
margin: 20px 0; }
|
18
|
+
|
19
|
+
ol, ul {
|
20
|
+
padding-left: 0;
|
21
|
+
margin-left: 30px; }
|
22
|
+
|
23
|
+
#all {
|
24
|
+
min-width: 960px;
|
25
|
+
box-sizing: border-box;
|
26
|
+
-moz-box-sizing: border-box;
|
27
|
+
-webkit-box-sizing: border-box;
|
28
|
+
width: 100%;
|
29
|
+
overflow-x: hidden;
|
30
|
+
padding: 40px; }
|
31
|
+
|
32
|
+
a, a:visited {
|
33
|
+
text-decoration: none;
|
34
|
+
border-bottom: solid 1px #ccf;
|
35
|
+
color: #46b; }
|
36
|
+
|
37
|
+
a:hover {
|
38
|
+
background: #fafae0; }
|
39
|
+
|
40
|
+
/* List */
|
41
|
+
ul li > p > strong:first-child {
|
42
|
+
display: block;
|
43
|
+
font-size: 1.1em; }
|
44
|
+
|
45
|
+
/* Headings */
|
46
|
+
h1 {
|
47
|
+
font-family: palatino, serif; }
|
48
|
+
|
49
|
+
h1, h2, h3, h4, h5, h6 {
|
50
|
+
text-shadow: 2px 2px 0 rgba(55, 55, 55, 0.1);
|
51
|
+
color: #568;
|
52
|
+
font-weight: normal;
|
53
|
+
font-family: shanti, palatino, serif; }
|
54
|
+
|
55
|
+
h3 {
|
56
|
+
color: #77a; }
|
57
|
+
|
58
|
+
h4, h5, h6 {
|
59
|
+
color: #88a; }
|
60
|
+
|
61
|
+
/* The "(class method)" thing */
|
62
|
+
h1 .type, h2 .type, h3 .type, h4 .type, h5 .type, h6 .type {
|
63
|
+
font-size: 9pt;
|
64
|
+
color: #aaa;
|
65
|
+
text-transform: uppercase;
|
66
|
+
margin-left: 10px; }
|
67
|
+
|
68
|
+
h2 {
|
69
|
+
max-width: none;
|
70
|
+
padding-top: 30px;
|
71
|
+
margin-top: 40px; }
|
72
|
+
|
73
|
+
h3 {
|
74
|
+
max-width: none;
|
75
|
+
padding-top: 10px;
|
76
|
+
margin-top: 40px; }
|
77
|
+
|
78
|
+
h1 { font-size: 30pt; }
|
79
|
+
h2 { font-size: 23pt; }
|
80
|
+
h3 { font-size: 14pt; }
|
81
|
+
h4 { font-size: 14pt; }
|
82
|
+
h5 { font-size: 10pt; }
|
83
|
+
h6 { font-size: 10pt; }
|
84
|
+
|
85
|
+
h2+p { margin-top: 0; }
|
86
|
+
h2+pre.right+p { margin-top: 0; }
|
87
|
+
|
88
|
+
/* Code */
|
89
|
+
pre, code {
|
90
|
+
font-family: Monaco, monospace; }
|
91
|
+
|
92
|
+
code {
|
93
|
+
text-shadow: 1px 1px 0 rgba(255, 255, 255, 0.99);
|
94
|
+
font-family: 'shanti', sans-serif;
|
95
|
+
color: #9090aa; }
|
96
|
+
|
97
|
+
pre > code {
|
98
|
+
color: #60606f;
|
99
|
+
text-shadow: none;
|
100
|
+
font-family: Monaco, monospace;
|
101
|
+
background: 0;
|
102
|
+
padding: 0;
|
103
|
+
border: 0; }
|
104
|
+
|
105
|
+
pre {
|
106
|
+
font-size: 9pt;
|
107
|
+
padding-top: 5px;
|
108
|
+
background: transparent; }
|
109
|
+
|
110
|
+
/* The header */
|
111
|
+
.header h1, .header h2, .header h3,
|
112
|
+
.header h4, .header h5, .header h6 {
|
113
|
+
text-shadow: none;
|
114
|
+
line-height: 1.35;
|
115
|
+
margin: 0;
|
116
|
+
padding: 0; }
|
117
|
+
|
118
|
+
.header h1 a,
|
119
|
+
.header h1 {
|
120
|
+
font-family: 'pt sans', sans-serif;
|
121
|
+
font-weight: bold;
|
122
|
+
color: #779; }
|
123
|
+
|
124
|
+
.header h1 a {
|
125
|
+
border-bottom: 0; }
|
126
|
+
|
127
|
+
.header h2, .header h3,
|
128
|
+
.header h4, .header h5, .header h6 {
|
129
|
+
color: #aab; } /* color: #78b; */
|
130
|
+
|
131
|
+
.header h4 {
|
132
|
+
font-size: 1.4em;
|
133
|
+
font-family: palatino, serif;
|
134
|
+
font-style: italic;
|
135
|
+
font-weight: normal; }
|
136
|
+
|
137
|
+
/* Literate */
|
138
|
+
body.literate h4, body.literate h5, body.literate h6, body.literate p,
|
139
|
+
body.literate ol, body.literate ul, body.literate pre {
|
140
|
+
max-width: 400px; }
|
141
|
+
|
142
|
+
body.literate pre.right {
|
143
|
+
clear: both;
|
144
|
+
max-width: none;
|
145
|
+
width: 490px;
|
146
|
+
margin-left: 50px;
|
147
|
+
float: right; }
|
148
|
+
|
149
|
+
body.literate pre.right code {
|
150
|
+
color: #78a; }
|
151
|
+
|
152
|
+
body.literate h1 + pre.right,
|
153
|
+
body.literate h2 + pre.right,
|
154
|
+
body.literate h3 + pre.right {
|
155
|
+
margin-top: 0; }
|
156
|
+
|
157
|
+
h1, h2, h3 {
|
158
|
+
clear: both; }
|
159
|
+
|
160
|
+
.clear {
|
161
|
+
clear: both; }
|
162
|
+
|
163
|
+
br.post-pre {
|
164
|
+
clear: both; }
|
165
|
+
|
166
|
+
h4 { margin-bottom: 5px; }
|
167
|
+
h4+p,
|
168
|
+
h4+pre.right,
|
169
|
+
h4+pre.right+p { margin-top: 0; }
|
170
|
+
|
171
|
+
/* Section-wrap */
|
172
|
+
section {
|
173
|
+
width: 960px;
|
174
|
+
margin-top: 0;
|
175
|
+
margin-left: 0;
|
176
|
+
padding-left: 40px;
|
177
|
+
margin-left: -40px;
|
178
|
+
padding-right: 9000px; }
|
179
|
+
|
180
|
+
section::after {
|
181
|
+
content: '';
|
182
|
+
display: table;
|
183
|
+
clear: both; }
|
184
|
+
|
185
|
+
section.h2:nth-child(even) {
|
186
|
+
box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1);
|
187
|
+
background: rgba(255, 255, 255, 0.8); }
|
188
|
+
|
189
|
+
section:first-of-type {
|
190
|
+
margin-top: 40px; }
|
191
|
+
|
192
|
+
section.h1, section.h2, section.h3 {
|
193
|
+
padding-top: 30px;
|
194
|
+
padding-bottom: 40px;
|
195
|
+
border-top: solid 1px #eee; }
|
196
|
+
|
197
|
+
section.h1 {
|
198
|
+
border-top: solid 10px #ddd; }
|
199
|
+
|
200
|
+
section.h3 {
|
201
|
+
padding-top: 20px;
|
202
|
+
margin-top: 40px;
|
203
|
+
padding-left: 30px;
|
204
|
+
border-left: solid 10px #e7e7f4;
|
205
|
+
border-top: dotted 1px #ddd; }
|
206
|
+
|
207
|
+
section.h3:nth-child(odd) {
|
208
|
+
border-left-color: #d0d0e8; }
|
209
|
+
|
210
|
+
section.h3 + section.h3 {
|
211
|
+
margin-top: -20px; }
|
212
|
+
|
213
|
+
section.h1>:first-child, section.h2>:first-child, section.h3>:first-child {
|
214
|
+
padding-top: 0;
|
215
|
+
margin-top: 0; }
|
216
|
+
|
217
|
+
/* API */
|
218
|
+
section.api h2,
|
219
|
+
section.api h3 {
|
220
|
+
margin-bottom: 0; }
|
221
|
+
|
222
|
+
section.api h2 + p,
|
223
|
+
section.api h2 + pre + p,
|
224
|
+
section.api h3 + p,
|
225
|
+
section.api h3 + pre + p {
|
226
|
+
margin-top: 0; }
|
227
|
+
|
228
|
+
h2 .args,
|
229
|
+
h3 .args {
|
230
|
+
margin-left: 3px;
|
231
|
+
font-size: 0.9em;
|
232
|
+
margin-right: 15px;
|
233
|
+
color: #aab; }
|
234
|
+
|
235
|
+
/* Pretty printing styles. Used with prettify.js. */
|
236
|
+
.str { color: #080; }
|
237
|
+
.kwd { color: #008; }
|
238
|
+
.com { color: #607077; background: rgba(0, 0, 0, 0.05); padding: 1px 3px; }
|
239
|
+
.typ { color: #606; }
|
240
|
+
.lit { color: #066; }
|
241
|
+
.pun { color: #660; }
|
242
|
+
.pln { color: #000; }
|
243
|
+
.tag { color: #008; }
|
244
|
+
.atn { color: #606; }
|
245
|
+
.atv { color: #080; }
|
246
|
+
.dec { color: #606; }
|
247
|
+
|
248
|
+
/* Specify class=linenums on a pre to get line numbering */
|
249
|
+
ol.linenums { margin-top: 0; margin-bottom: 0 } /* IE indents via margin-left */
|
250
|
+
li.L0, li.L1, li.L2, li.L3, li.L5, li.L6, li.L7, li.L8 { list-style-type: none }
|
251
|
+
/* Alternate shading for lines */
|
252
|
+
li.L1, li.L3, li.L5, li.L7, li.L9 { background: #eee }
|
253
|
+
|
254
|
+
@media print {
|
255
|
+
.str { color: #060; }
|
256
|
+
.kwd { color: #006; font-weight: bold; }
|
257
|
+
.com { color: #600; font-style: italic; }
|
258
|
+
.typ { color: #404; font-weight: bold; }
|
259
|
+
.lit { color: #044; }
|
260
|
+
.pun { color: #440; }
|
261
|
+
.pln { color: #000; }
|
262
|
+
.tag { color: #006; font-weight: bold; }
|
263
|
+
.atn { color: #404; }
|
264
|
+
.atv { color: #060; }
|
265
|
+
}
|
266
|
+
|
267
|
+
/* TOC */
|
268
|
+
body.toc {
|
269
|
+
margin-left: 200px; }
|
270
|
+
|
271
|
+
aside#toc {
|
272
|
+
font-size: 0.9em;
|
273
|
+
line-height: 1.7;
|
274
|
+
position: fixed;
|
275
|
+
top: 0;
|
276
|
+
left: 0;
|
277
|
+
bottom: 0;
|
278
|
+
padding: 20px;
|
279
|
+
width: 180px;
|
280
|
+
overflow-y: auto;
|
281
|
+
box-shadow: inset -2px 0 2px rgba(0, 0, 0, 0.1);
|
282
|
+
border-right: solid 1px #ccc;
|
283
|
+
background: #f4f4fa; }
|
284
|
+
|
285
|
+
aside#toc h1,
|
286
|
+
aside#toc h2,
|
287
|
+
aside#toc h3 {
|
288
|
+
overflow: hidden;
|
289
|
+
margin: 10px 0;
|
290
|
+
padding: 0;
|
291
|
+
font-size: 1.1em; }
|
292
|
+
|
293
|
+
aside#toc h1 {
|
294
|
+
color: #aaa;
|
295
|
+
text-shadow: none;
|
296
|
+
font-size: 1.3em; }
|
297
|
+
|
298
|
+
aside#toc h3 {
|
299
|
+
border-top: solid 1px #ddd;
|
300
|
+
padding-top: 10px; }
|
301
|
+
|
302
|
+
aside#toc h2 .type,
|
303
|
+
aside#toc h3 .type {
|
304
|
+
margin-left: 0; }
|
305
|
+
|
306
|
+
aside#toc ul,
|
307
|
+
aside#toc li {
|
308
|
+
margin: 0;
|
309
|
+
padding: 0;
|
310
|
+
list-style-type: none; }
|
311
|
+
|
312
|
+
aside#toc h1 a,
|
313
|
+
aside#toc h2 a,
|
314
|
+
aside#toc h3 a {
|
315
|
+
color: #333; }
|
316
|
+
|
317
|
+
aside#toc a {
|
318
|
+
overflow: hidden;
|
319
|
+
display: block;
|
320
|
+
border-bottom: 0;
|
321
|
+
color: #555; }
|
322
|
+
|
323
|
+
aside#toc a span.args {
|
324
|
+
display: none; }
|
325
|
+
|
326
|
+
aside#toc a span.type {
|
327
|
+
font-size: 0.8em;
|
328
|
+
text-transform: uppercase;
|
329
|
+
color: #999; }
|
330
|
+
|
331
|
+
aside#toc .type {
|
332
|
+
float: right; }
|
333
|
+
|
334
|
+
aside#toc::-webkit-scrollbar {
|
335
|
+
height: 15px; width: 15px;
|
336
|
+
}
|
337
|
+
|
338
|
+
aside#toc::-webkit-scrollbar-thumb {
|
339
|
+
border-width: 7px 7px 7px 7px;
|
340
|
+
-webkit-border-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NDZEMERCMUZGRUI2MTFERkIwQ0RGMkFBNEE3NDlCOUMiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NDZEMERCMjBGRUI2MTFERkIwQ0RGMkFBNEE3NDlCOUMiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo0NkQwREIxREZFQjYxMURGQjBDREYyQUE0QTc0OUI5QyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo0NkQwREIxRUZFQjYxMURGQjBDREYyQUE0QTc0OUI5QyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PpHo8kwAAACBSURBVHjaYvz//z8DJYCJgUJAsQEsyBxGRkYwBcS6QKwBxLJQqcdAfAOILwPxf2RvM6JwICbYA7EDDgsPAPFBZBPQvaCLRzMDVE4XXxhoEOFtDXwGyBJhgCxNo/ExEXoe4zPgBhEG3MBnwGVoVDHgicbLKGkHLR1QlpCGZmYCCDAAxuMoE4WhhWUAAAAASUVORK5CYII=) 7 7 7 7 round round;
|
341
|
+
}
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# Reacco::Extractor::Block [class]
|
2
|
+
# An extractor block.
|
3
|
+
#
|
4
|
+
module Reacco
|
5
|
+
class Extractor
|
6
|
+
class Block
|
7
|
+
# body [method]
|
8
|
+
# Returns the body text as a raw string.
|
9
|
+
attr_reader :body
|
10
|
+
|
11
|
+
# title [method]
|
12
|
+
# Returns the title of the block.
|
13
|
+
attr_reader :title
|
14
|
+
attr_reader :type
|
15
|
+
attr_reader :parent
|
16
|
+
attr_reader :args
|
17
|
+
|
18
|
+
def initialize(options)
|
19
|
+
@body = options[:body]
|
20
|
+
@type = options[:type] && options[:type].downcase
|
21
|
+
@args = options[:args]
|
22
|
+
@title = options[:title]
|
23
|
+
@parent = options[:parent]
|
24
|
+
@tag = options[:tag]
|
25
|
+
@source_line = options[:source_line]
|
26
|
+
@source_path = options[:source_path]
|
27
|
+
end
|
28
|
+
|
29
|
+
# children [method]
|
30
|
+
# Returns an array of child blocks.
|
31
|
+
def children
|
32
|
+
@children ||= Array.new
|
33
|
+
end
|
34
|
+
|
35
|
+
# << [method]
|
36
|
+
# Adds a block to it's children.
|
37
|
+
def <<(blk)
|
38
|
+
children << blk
|
39
|
+
end
|
40
|
+
|
41
|
+
def raw_html
|
42
|
+
Reacco.markdown.render(@body)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Nokogiri node.
|
46
|
+
def doc
|
47
|
+
@doc ||= Nokogiri::HTML(raw_html)
|
48
|
+
end
|
49
|
+
|
50
|
+
def transform!
|
51
|
+
# Create heading.
|
52
|
+
name = @tag
|
53
|
+
node = Nokogiri::XML::Node.new(name, doc)
|
54
|
+
node['class'] = 'api'
|
55
|
+
node.content = title
|
56
|
+
|
57
|
+
# Add '(args)'.
|
58
|
+
if args
|
59
|
+
span = Nokogiri::XML::Node.new("span", doc)
|
60
|
+
span['class'] = 'args'
|
61
|
+
span.content = args
|
62
|
+
node.add_child span
|
63
|
+
end
|
64
|
+
|
65
|
+
# Add '(class method)'.
|
66
|
+
span = Nokogiri::XML::Node.new("span", doc)
|
67
|
+
span['class'] = 'type'
|
68
|
+
span.content = type
|
69
|
+
node.add_child Nokogiri::XML::Text.new(' ', doc)
|
70
|
+
node.add_child span
|
71
|
+
|
72
|
+
# Add heading.
|
73
|
+
doc.at_css('body>*:first-child').add_previous_sibling node
|
74
|
+
doc
|
75
|
+
end
|
76
|
+
|
77
|
+
# to_html [method]
|
78
|
+
# Returns the raw HTML to be included in the documentation.
|
79
|
+
def to_html
|
80
|
+
@to_html ||= transform!.at_css('body').inner_html
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'ostruct'
|
4
|
+
require 'fileutils'
|
5
|
+
module Reacco
|
6
|
+
# Reacco::Extractor [class]
|
7
|
+
# Extracts comments from list of files.
|
8
|
+
#
|
9
|
+
# #### Instantiating
|
10
|
+
# Call the constructor with a list of files.
|
11
|
+
#
|
12
|
+
# ex = Extractor.new(Dir['./**/*.rb'])
|
13
|
+
#
|
14
|
+
class Extractor
|
15
|
+
autoload :Block, 'reacco/extractor/block'
|
16
|
+
|
17
|
+
def initialize(files, root=nil, options={})
|
18
|
+
@files = files.sort
|
19
|
+
@root = File.realpath(root || Dir.pwd)
|
20
|
+
end
|
21
|
+
|
22
|
+
# blocks [method]
|
23
|
+
# Returns an array of `Block` instances.
|
24
|
+
#
|
25
|
+
# ex.blocks
|
26
|
+
# ex.blocks.map { |b| puts "file: #{b.file}" }
|
27
|
+
#
|
28
|
+
def blocks
|
29
|
+
@blocks ||= begin
|
30
|
+
@files.map do |file|
|
31
|
+
if File.file?(file)
|
32
|
+
input = File.read(file)
|
33
|
+
get_blocks(input, unroot(file))
|
34
|
+
end
|
35
|
+
end.compact.flatten
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
def unroot(fn)
|
41
|
+
(File.realpath(fn))[@root.size..-1]
|
42
|
+
end
|
43
|
+
|
44
|
+
# get_blocks(str, [filename]) [private method]
|
45
|
+
# Returns the documentation blocks in a given `str`ing.
|
46
|
+
# If `filename` is given, it will be set as the *source_file*.
|
47
|
+
#
|
48
|
+
def get_blocks(str, filename=nil)
|
49
|
+
arr = get_comment_blocks(str)
|
50
|
+
|
51
|
+
arr.map do |hash|
|
52
|
+
block = hash[:block]
|
53
|
+
|
54
|
+
# Ensure the first line matches.
|
55
|
+
# This matches:
|
56
|
+
# "### name [type]"
|
57
|
+
# "## name(args) [type]"
|
58
|
+
re = /^(\#{1,6}) (.*?) ?(\(.*?\))? ?(?:\[([a-z ]+)\])?$/
|
59
|
+
block.first =~ re or next
|
60
|
+
|
61
|
+
blk = Extractor::Block.new \
|
62
|
+
:type => $4,
|
63
|
+
:tag => "h#{$1.strip.size}",
|
64
|
+
:title => $2,
|
65
|
+
:args => $3,
|
66
|
+
:source_line => hash[:line] + block.size + 1,
|
67
|
+
:source_file => filename,
|
68
|
+
:body => (block[1..-1].join("\n") + "\n")
|
69
|
+
|
70
|
+
blk
|
71
|
+
end.compact
|
72
|
+
end
|
73
|
+
|
74
|
+
# get_comment_blocks (private method)
|
75
|
+
# Returns contiguous comment blocks.
|
76
|
+
#
|
77
|
+
# Returns an array of hashes that look like
|
78
|
+
# `{ :block => [line1, line2...], :line => (line number) }`
|
79
|
+
#
|
80
|
+
def get_comment_blocks(str)
|
81
|
+
chunks = Array.new
|
82
|
+
i = 0
|
83
|
+
|
84
|
+
str.split("\n").each_with_index { |s, line|
|
85
|
+
if s =~ /^\s*(?:\/\/\/?|##?) ?(.*)$/
|
86
|
+
chunks[i] ||= { :block => Array.new, :line => line }
|
87
|
+
chunks[i][:block] << $1
|
88
|
+
else
|
89
|
+
i += 1 if chunks[i]
|
90
|
+
end
|
91
|
+
}
|
92
|
+
|
93
|
+
chunks.compact
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Reacco
|
2
|
+
module Filters
|
3
|
+
module Hgroup
|
4
|
+
# Wraps the first headings into an <hgroup>.
|
5
|
+
def wrap_hgroup(html)
|
6
|
+
nodes = Array.new
|
7
|
+
html.css('body>*').each { |node|
|
8
|
+
# Consume all headings.
|
9
|
+
if %w(h1 h2 h3 h4 h5 h6).include?(node.name)
|
10
|
+
nodes << node.to_s
|
11
|
+
node.remove
|
12
|
+
|
13
|
+
# Once the headings stop, dump them into an <hgroup>.
|
14
|
+
# Ah, and eat an <hr> if there is one.
|
15
|
+
else
|
16
|
+
node.before("<hgroup class='header'>#{nodes.join('')}</hgroup>") if nodes.any?
|
17
|
+
node.remove if node.name == 'hr'
|
18
|
+
break
|
19
|
+
end
|
20
|
+
}
|
21
|
+
|
22
|
+
html
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Reacco
|
2
|
+
module Filters
|
3
|
+
module Literate
|
4
|
+
# Moves pre's after headers.
|
5
|
+
def move_pre(html)
|
6
|
+
anchor = nil
|
7
|
+
position = nil
|
8
|
+
|
9
|
+
html.css('body>*').each_cons(2) { |(node, nxt)|
|
10
|
+
# Once we find the <pre>, move it.
|
11
|
+
if node.name == 'pre' && anchor && anchor != node && !(node['class'].to_s =~ /full/)
|
12
|
+
node.after "<br class='post-pre'>"
|
13
|
+
|
14
|
+
nxt['class'] = "#{nxt['class']} after-pre" if nxt
|
15
|
+
node['class'] = "#{node['class']} right"
|
16
|
+
|
17
|
+
anchor.send position, node
|
18
|
+
anchor = nil
|
19
|
+
|
20
|
+
# If we find one of these, put the next <pre> after it.
|
21
|
+
elsif %w(h1 h2 h3 h4 pre).include?(node.name)
|
22
|
+
anchor = node
|
23
|
+
position = :add_next_sibling
|
24
|
+
|
25
|
+
# If we find one of these, put the <pre> before it.
|
26
|
+
elsif node['class'].to_s.include?('after-pre')
|
27
|
+
anchor = node
|
28
|
+
position = :add_previous_sibling
|
29
|
+
end
|
30
|
+
}
|
31
|
+
|
32
|
+
html
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Reacco
|
2
|
+
module Filters
|
3
|
+
module Sections
|
4
|
+
# Wraps in sections.
|
5
|
+
def section_wrap(html)
|
6
|
+
%w(h1 h2 h3 h4 h5).each do |h|
|
7
|
+
nodes = html.css(h)
|
8
|
+
nodes.each do |alpha|
|
9
|
+
# For those affected by --hgroup, don't bother.
|
10
|
+
next if alpha.ancestors.any? { |tag| tag.name == 'hgroup' }
|
11
|
+
next unless alpha.parent
|
12
|
+
|
13
|
+
# Find the boundary, and get the nodes until that one.
|
14
|
+
omega = from_x_until(alpha, alpha.name)
|
15
|
+
section_nodes = between(alpha, omega)
|
16
|
+
|
17
|
+
# Create the <section>.
|
18
|
+
section = Nokogiri::XML::Node.new('section', html)
|
19
|
+
section['class'] = "#{alpha['class']} #{h} #{slugify alpha.content}"
|
20
|
+
alpha.add_previous_sibling(section)
|
21
|
+
section_nodes.each { |tag| section.add_child tag }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
html
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def from_x_until(alpha, name)
|
30
|
+
omega = nil
|
31
|
+
n = alpha
|
32
|
+
|
33
|
+
while true
|
34
|
+
n = n.next_sibling
|
35
|
+
break if n.nil? || n.name == name
|
36
|
+
omega = n
|
37
|
+
end
|
38
|
+
|
39
|
+
omega
|
40
|
+
end
|
41
|
+
|
42
|
+
def between(first, last)
|
43
|
+
nodes = Array.new
|
44
|
+
started = false
|
45
|
+
|
46
|
+
first.parent.children.each do |node|
|
47
|
+
started = true if node == first
|
48
|
+
nodes << node if started
|
49
|
+
break if node == last
|
50
|
+
end
|
51
|
+
|
52
|
+
nodes
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Reacco
|
2
|
+
module Filters
|
3
|
+
module TOC
|
4
|
+
def make_toc(contents)
|
5
|
+
aside = Nokogiri::XML::Node.new('aside', contents)
|
6
|
+
aside['id'] = 'toc'
|
7
|
+
|
8
|
+
# Header
|
9
|
+
title = (h1 = contents.at_css('h1')) && h1.text || File.basename(Dir.pwd)
|
10
|
+
aside.inner_html = "#{aside.inner_html}<h1>#{title}</h1>"
|
11
|
+
|
12
|
+
contents.xpath('//body/section').each { |tag|
|
13
|
+
aside.inner_html = "#{aside.inner_html}#{make_section tag}"
|
14
|
+
}
|
15
|
+
|
16
|
+
contents.at_css('body').add_child aside
|
17
|
+
|
18
|
+
contents
|
19
|
+
end
|
20
|
+
|
21
|
+
def linkify(h)
|
22
|
+
href = slugify(h.content)
|
23
|
+
"<a href='##{href}'>#{h.inner_html}</a>"
|
24
|
+
end
|
25
|
+
|
26
|
+
def make_section(section)
|
27
|
+
h = section.at_css('h1, h2, h3')
|
28
|
+
return '' unless h
|
29
|
+
level = h.name[1] # 1 | 2 | 3
|
30
|
+
return '' unless %w(1 2 3).include?(level)
|
31
|
+
name = h.content.strip
|
32
|
+
|
33
|
+
out = case level
|
34
|
+
when "1"
|
35
|
+
[ "<h2>#{linkify h}</h2>",
|
36
|
+
section.css('section.h2').map { |s| make_section s }
|
37
|
+
]
|
38
|
+
when "2"
|
39
|
+
[ "<nav class='level-#{level}'>",
|
40
|
+
"<h3>#{linkify h}</h3>",
|
41
|
+
"<ul>",
|
42
|
+
section.css('section.h3').map { |s| make_section s },
|
43
|
+
"</ul>",
|
44
|
+
"</nav>"
|
45
|
+
]
|
46
|
+
when "3"
|
47
|
+
[ "<li>#{linkify h}</li>" ]
|
48
|
+
end
|
49
|
+
|
50
|
+
out.flatten.join "\n"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Reacco
|
2
|
+
class Generator
|
3
|
+
def initialize(readme, dir, template=nil, css=nil)
|
4
|
+
@readme = readme
|
5
|
+
@dir = dir
|
6
|
+
@css = css
|
7
|
+
@template = template || Reacco.root('data')
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns the HTML contents.
|
11
|
+
def html
|
12
|
+
file = Dir["#{@template}/index.*"].first
|
13
|
+
raise "No index file found in template path." unless file
|
14
|
+
|
15
|
+
tpl = Tilt.new(file)
|
16
|
+
out = tpl.render({}, @readme.locals) { @readme.html }
|
17
|
+
end
|
18
|
+
|
19
|
+
# Writes to the output directory.
|
20
|
+
def write!(&blk)
|
21
|
+
yield "#{@dir}/index.html"
|
22
|
+
write_to "#{@dir}/index.html", html
|
23
|
+
copy_files &blk
|
24
|
+
append_css if @css
|
25
|
+
end
|
26
|
+
|
27
|
+
def template?
|
28
|
+
File.directory?(@template) && Dir["#{@template}/index.*"].first
|
29
|
+
end
|
30
|
+
|
31
|
+
# Adds the CSS file to the style.css file.
|
32
|
+
def append_css
|
33
|
+
contents = File.read(@css)
|
34
|
+
File.open("#{@dir}/style.css", 'a+') { |f| f.write "\n/* Custom */\n#{contents}" }
|
35
|
+
end
|
36
|
+
|
37
|
+
def copy_files(&blk)
|
38
|
+
files = Dir.chdir(@template) { Dir['**/*'] }
|
39
|
+
|
40
|
+
# For each of the template files...
|
41
|
+
files.each do |f|
|
42
|
+
next if File.basename(f)[0] == '_'
|
43
|
+
next if File.fnmatch('index.*', f)
|
44
|
+
ext = File.extname(f)[1..-1]
|
45
|
+
|
46
|
+
fullpath = File.join @template, f
|
47
|
+
|
48
|
+
# Try to render it with Tilt if possible.
|
49
|
+
if Tilt.mappings.keys.include?(ext)
|
50
|
+
contents = Tilt.new(fullpath).render
|
51
|
+
outfile = f.match(/^(.*)(\.[^\.]+)$/) && $1
|
52
|
+
else
|
53
|
+
contents = File.read(fullpath)
|
54
|
+
outfile = f
|
55
|
+
end
|
56
|
+
|
57
|
+
yield "#{@dir}/#{outfile}"
|
58
|
+
write_to "#{@dir}/#{outfile}", contents
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
def write_to(path, data)
|
64
|
+
FileUtils.mkdir_p File.dirname(path)
|
65
|
+
File.open(path, 'w') { |f| f.write data }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
# ## Reacco::Readme [class]
|
2
|
+
# A readme file.
|
3
|
+
module Reacco
|
4
|
+
class Readme
|
5
|
+
def initialize(options={})
|
6
|
+
@file = options.delete(:file) if options[:file]
|
7
|
+
@options = options
|
8
|
+
end
|
9
|
+
|
10
|
+
# ### file [method]
|
11
|
+
# The path to the README file. Returns nil if not available.
|
12
|
+
def file
|
13
|
+
@file ||= (Dir['README.*'].first || Dir['readme.*'].first || Dir['README'].first)
|
14
|
+
end
|
15
|
+
|
16
|
+
def file=(file)
|
17
|
+
@file = file
|
18
|
+
end
|
19
|
+
|
20
|
+
# ### switches [method]
|
21
|
+
# The switches, like 'literate' and 'hgroup'. Returns an array of strings.
|
22
|
+
def switches
|
23
|
+
@options.keys.map { |k| k.to_s }
|
24
|
+
end
|
25
|
+
|
26
|
+
# ### exists? [method]
|
27
|
+
# Returns true if the file (given in the constructor) exists.
|
28
|
+
def exists?
|
29
|
+
file && File.exists?(file)
|
30
|
+
end
|
31
|
+
|
32
|
+
# ### raw [method]
|
33
|
+
# Returns raw Markdown markup.
|
34
|
+
def raw
|
35
|
+
@raw ||= File.read(file)
|
36
|
+
end
|
37
|
+
|
38
|
+
# ### title [method]
|
39
|
+
# Returns a string of the title of the document (the first h1).
|
40
|
+
def title
|
41
|
+
@title ||= begin
|
42
|
+
h1 = (h1 = doc.at_css('h1')) && h1.text
|
43
|
+
h1 || File.basename(Dir.pwd)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def title?
|
48
|
+
title.to_s.size > 0
|
49
|
+
end
|
50
|
+
|
51
|
+
# ### raw_html [method]
|
52
|
+
# Raw HTML data.
|
53
|
+
def raw_html
|
54
|
+
@raw_html ||= markdown.render(raw)
|
55
|
+
end
|
56
|
+
|
57
|
+
def raw_html=(str)
|
58
|
+
@raw_html = str
|
59
|
+
end
|
60
|
+
|
61
|
+
# ### inject_api_block [method]
|
62
|
+
# Adds an API block. Takes an `html` argument.
|
63
|
+
def inject_api_block(html)
|
64
|
+
@api_blocks = "#{api_blocks}\n#{html}\n"
|
65
|
+
end
|
66
|
+
|
67
|
+
def api_blocks
|
68
|
+
@api_blocks ||= ""
|
69
|
+
end
|
70
|
+
|
71
|
+
# ### doc [method]
|
72
|
+
# Returns HTML as a Nokogiri document.
|
73
|
+
def doc(options={})
|
74
|
+
@doc ||= begin
|
75
|
+
add_api(api_blocks)
|
76
|
+
html = Nokogiri::HTML(raw_html)
|
77
|
+
|
78
|
+
html = pre_lang(html)
|
79
|
+
html = heading_id(html)
|
80
|
+
html = wrap_hgroup(html)
|
81
|
+
html = move_pre(html) if @options[:literate]
|
82
|
+
html = brief_first_p(html)
|
83
|
+
html = section_wrap(html)
|
84
|
+
html = make_toc(html) if @options[:toc]
|
85
|
+
|
86
|
+
html
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# ### html [method]
|
91
|
+
# Returns body's inner HTML.
|
92
|
+
def html
|
93
|
+
doc.at_css('body').inner_html
|
94
|
+
end
|
95
|
+
|
96
|
+
# ### github [method]
|
97
|
+
# Returns the GitHub URL, or nil if not applicable.
|
98
|
+
def github
|
99
|
+
"https://github.com/#{@options[:github]}" if @options[:github]
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns locals for the template.
|
103
|
+
def locals
|
104
|
+
{ :title => title,
|
105
|
+
:body_class => switches.join(' '),
|
106
|
+
:github => github }
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
include Filters::Brief
|
111
|
+
include Filters::Sections
|
112
|
+
include Filters::Hgroup
|
113
|
+
include Filters::Literate
|
114
|
+
include Filters::PreLang
|
115
|
+
include Filters::HeadingID
|
116
|
+
include Filters::TOC
|
117
|
+
|
118
|
+
# Puts `blocks` inside `raw_html`.
|
119
|
+
def add_api(blocks)
|
120
|
+
re1 = %r{^.*api reference goes here.*$}i
|
121
|
+
re2 = %r{^.*#api_reference.*$}i
|
122
|
+
|
123
|
+
if raw_html =~ re1
|
124
|
+
raw_html.gsub! re1, blocks
|
125
|
+
elsif raw_html =~ re2
|
126
|
+
raw_html.gsub! re2, blocks
|
127
|
+
else
|
128
|
+
self.raw_html = "#{raw_html}\n#{blocks}"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# ### markdown [private method]
|
133
|
+
# Returns the Markdown processor.
|
134
|
+
#
|
135
|
+
# markdown.render(md)
|
136
|
+
#
|
137
|
+
def markdown
|
138
|
+
Reacco.markdown
|
139
|
+
end
|
140
|
+
|
141
|
+
# ### slugify [private method]
|
142
|
+
# Turns text into a slug.
|
143
|
+
#
|
144
|
+
# "Install instructions" => "install_instructions"
|
145
|
+
#
|
146
|
+
def slugify(str)
|
147
|
+
str.downcase.scan(/[a-z0-9\-]+/).join('_')
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
data/lib/reacco.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'redcarpet'
|
3
|
+
require 'tilt'
|
4
|
+
require 'ostruct'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
# ## Reacco [module]
|
8
|
+
# This is the main module.
|
9
|
+
#
|
10
|
+
module Reacco
|
11
|
+
extend self
|
12
|
+
|
13
|
+
# ### root [class method]
|
14
|
+
# Returns the root path of the Reacco gem.
|
15
|
+
# You may pass additional parameters.
|
16
|
+
#
|
17
|
+
# Reacco.root #=> '/usr/local/ruby/gems/reacco-0.0.1'
|
18
|
+
#
|
19
|
+
def root(*a)
|
20
|
+
File.join File.expand_path('../../', __FILE__), *a
|
21
|
+
end
|
22
|
+
|
23
|
+
# ### markdown [class method]
|
24
|
+
# Returns the Redcarpet Markdown processor.
|
25
|
+
def markdown
|
26
|
+
Redcarpet::Markdown.new(Redcarpet::Render::HTML,
|
27
|
+
:fenced_code_blocks => true)
|
28
|
+
end
|
29
|
+
|
30
|
+
autoload :Readme, 'reacco/readme'
|
31
|
+
autoload :Generator, 'reacco/generator'
|
32
|
+
autoload :Extractor, 'reacco/extractor'
|
33
|
+
|
34
|
+
module Filters
|
35
|
+
autoload :Brief, 'reacco/filters/brief'
|
36
|
+
autoload :Sections, 'reacco/filters/sections'
|
37
|
+
autoload :Hgroup, 'reacco/filters/hgroup'
|
38
|
+
autoload :Literate, 'reacco/filters/literate'
|
39
|
+
autoload :PreLang, 'reacco/filters/prelang'
|
40
|
+
autoload :HeadingID, 'reacco/filters/headingid'
|
41
|
+
autoload :TOC, 'reacco/filters/toc'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
data/reacco.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require './lib/reacco/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "reacco"
|
5
|
+
s.version = Reacco.version
|
6
|
+
s.summary = %{Readme prettifier.}
|
7
|
+
s.description = %Q{Reacco makes your readme's pretty.}
|
8
|
+
s.authors = ["Rico Sta. Cruz"]
|
9
|
+
s.email = ["rico@sinefunc.com"]
|
10
|
+
s.homepage = "http://github.com/rstacruz/reacco"
|
11
|
+
s.files = `git ls-files`.strip.split("\n")
|
12
|
+
s.executables = Dir["bin/*"].map { |f| File.basename(f) }
|
13
|
+
|
14
|
+
s.add_dependency 'redcarpet', '~> 2.0.0b3'
|
15
|
+
s.add_dependency 'nokogiri', '~> 1.5'
|
16
|
+
s.add_dependency 'tilt'
|
17
|
+
end
|
data/test/blocks_test.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class BlocksTest < UnitTest
|
4
|
+
setup do
|
5
|
+
@ex = Reacco::Extractor.new(Dir[root 'lib/**/*.rb'])
|
6
|
+
end
|
7
|
+
|
8
|
+
should "extract comments" do
|
9
|
+
# From Reacco.root
|
10
|
+
block = @ex.blocks.detect { |blk| blk.title == "root" && blk.type == "class method" }
|
11
|
+
assert ! block.nil?
|
12
|
+
end
|
13
|
+
|
14
|
+
should "htmlize properly" do
|
15
|
+
block = @ex.blocks.detect { |blk| blk.title == "root" && blk.type == "class method" }
|
16
|
+
p block.to_html
|
17
|
+
end
|
18
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: reacco
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Rico Sta. Cruz
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-09-18 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: redcarpet
|
16
|
+
requirement: &2156349300 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.0.0b3
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2156349300
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: nokogiri
|
27
|
+
requirement: &2156348320 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '1.5'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *2156348320
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: tilt
|
38
|
+
requirement: &2156347220 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *2156347220
|
47
|
+
description: Reacco makes your readme's pretty.
|
48
|
+
email:
|
49
|
+
- rico@sinefunc.com
|
50
|
+
executables:
|
51
|
+
- reacco
|
52
|
+
extensions: []
|
53
|
+
extra_rdoc_files: []
|
54
|
+
files:
|
55
|
+
- .gitignore
|
56
|
+
- Gemfile
|
57
|
+
- HISTORY.md
|
58
|
+
- README.md
|
59
|
+
- Rakefile
|
60
|
+
- bin/reacco
|
61
|
+
- data/index.erb
|
62
|
+
- data/style.css
|
63
|
+
- lib/reacco.rb
|
64
|
+
- lib/reacco/extractor.rb
|
65
|
+
- lib/reacco/extractor/block.rb
|
66
|
+
- lib/reacco/filters/brief.rb
|
67
|
+
- lib/reacco/filters/headingid.rb
|
68
|
+
- lib/reacco/filters/hgroup.rb
|
69
|
+
- lib/reacco/filters/literate.rb
|
70
|
+
- lib/reacco/filters/prelang.rb
|
71
|
+
- lib/reacco/filters/sections.rb
|
72
|
+
- lib/reacco/filters/toc.rb
|
73
|
+
- lib/reacco/generator.rb
|
74
|
+
- lib/reacco/readme.rb
|
75
|
+
- lib/reacco/version.rb
|
76
|
+
- reacco.gemspec
|
77
|
+
- test/blocks_test.rb
|
78
|
+
- test/test_helper.rb
|
79
|
+
homepage: http://github.com/rstacruz/reacco
|
80
|
+
licenses: []
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ! '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
none: false
|
93
|
+
requirements:
|
94
|
+
- - ! '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
requirements: []
|
98
|
+
rubyforge_project:
|
99
|
+
rubygems_version: 1.8.10
|
100
|
+
signing_key:
|
101
|
+
specification_version: 3
|
102
|
+
summary: Readme prettifier.
|
103
|
+
test_files: []
|