rundoc 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +38 -0
- data/README.md +309 -0
- data/Rakefile +16 -0
- data/bin/rundoc +82 -0
- data/lib/rundoc.rb +76 -0
- data/lib/rundoc/code_command.rb +32 -0
- data/lib/rundoc/code_command/bash.rb +57 -0
- data/lib/rundoc/code_command/bash/cd.rb +18 -0
- data/lib/rundoc/code_command/file_command/append.rb +74 -0
- data/lib/rundoc/code_command/file_command/remove.rb +32 -0
- data/lib/rundoc/code_command/no_such_command.rb +6 -0
- data/lib/rundoc/code_command/pipe.rb +37 -0
- data/lib/rundoc/code_command/repl.rb +37 -0
- data/lib/rundoc/code_command/rundoc_command.rb +22 -0
- data/lib/rundoc/code_command/write.rb +34 -0
- data/lib/rundoc/code_section.rb +145 -0
- data/lib/rundoc/parser.rb +53 -0
- data/lib/rundoc/version.rb +3 -0
- data/rundoc.gemspec +28 -0
- data/test/fixtures/play/source.md +231 -0
- data/test/fixtures/rails_4/rundoc.md +504 -0
- data/test/rundoc/code_commands/append_file_test.rb +55 -0
- data/test/rundoc/code_commands/bash_test.rb +29 -0
- data/test/rundoc/code_commands/pipe_test.rb +15 -0
- data/test/rundoc/code_commands/remove_contents_test.rb +42 -0
- data/test/rundoc/code_section_test.rb +42 -0
- data/test/rundoc/parser_test.rb +104 -0
- data/test/rundoc/regex_test.rb +200 -0
- data/test/rundoc/test_parse_java.rb +8 -0
- data/test/test_helper.rb +15 -0
- metadata +144 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1e570f8645e887ed3c6cfd9a93cfc5933b57734a
|
4
|
+
data.tar.gz: e6925f4cbcb8e5aa68711a6918e6d44ae0f2c788
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ee03707c8cf0eb97d92683b3f0d49ad6d296807c607b58e75cb4e420fbbbea09c78f89265d8a64d52f7979230e5d43d05502421ca80d9478f795c60a51f57ee6
|
7
|
+
data.tar.gz: edd4a92ac3c1bac6e6c210ab37788003e2de1310f9615e5b74ac448dcff1c710d89ee51ed38d806b752bb46d6808f26809bdbc27c1cf4d3371ba4caefb252c72
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
rundoc (0.0.1)
|
5
|
+
repl_runner
|
6
|
+
thor
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: http://rubygems.org/
|
10
|
+
specs:
|
11
|
+
activesupport (4.0.0)
|
12
|
+
i18n (~> 0.6, >= 0.6.4)
|
13
|
+
minitest (~> 4.2)
|
14
|
+
multi_json (~> 1.3)
|
15
|
+
thread_safe (~> 0.1)
|
16
|
+
tzinfo (~> 0.3.37)
|
17
|
+
atomic (1.1.14)
|
18
|
+
i18n (0.6.5)
|
19
|
+
metaclass (0.0.1)
|
20
|
+
minitest (4.7.5)
|
21
|
+
mocha (0.14.0)
|
22
|
+
metaclass (~> 0.0.1)
|
23
|
+
multi_json (1.8.0)
|
24
|
+
rake (10.1.0)
|
25
|
+
repl_runner (0.0.2)
|
26
|
+
activesupport
|
27
|
+
thor (0.18.1)
|
28
|
+
thread_safe (0.1.3)
|
29
|
+
atomic
|
30
|
+
tzinfo (0.3.37)
|
31
|
+
|
32
|
+
PLATFORMS
|
33
|
+
ruby
|
34
|
+
|
35
|
+
DEPENDENCIES
|
36
|
+
mocha
|
37
|
+
rake
|
38
|
+
rundoc!
|
data/README.md
ADDED
@@ -0,0 +1,309 @@
|
|
1
|
+
## Rundoc
|
2
|
+
|
3
|
+
## What
|
4
|
+
|
5
|
+
Write more technical content, faster and with a better consistency by using rundoc. Write in a rundoc compatible markdown format, then run your docs to generate matching projects. Instead of writing a tutorial and then building an example separately, your documentation can build the example app for you. Not only does this keep your doc writing DRY, it also enforces consistency and accuracy. If you make a typo in your docs your project won't build...you'll get early warning and be able to fix it before it has the opportunity to confuse your reader.
|
6
|
+
|
7
|
+
Think of rundoc as your ever-vigilant tech editor and writing partner.
|
8
|
+
|
9
|
+
Once docs are run, they output a project and fully valid markdown doc (without any of the special rundoc tags). You could configure your project to be automatically pushed to github or anything else you want afterwards, check out the config section.
|
10
|
+
|
11
|
+
## Why
|
12
|
+
|
13
|
+
I wrote a [Rails course for the University of Texas](http://schneems.com), that required I build an app and write
|
14
|
+
documentation at the same time. I enjoyed the experience but having to do both essentially doubled my work load, worse than the time wasted copying snippets between the two was my docs were prone to paste errors, keyboard slips, or me just forgetting to add sections that I had implemented in the app. The only way for me to find these errors was to give the docs to someone to actually follow them and build the project. This method of manually checking is extremely time consuming, prone to errors (the developer may work around problems instead of reporting them to you), and makes making minor edits a major pain. Instead of writing your docs once and iterating, I found adding sections required me to start from scratch.
|
15
|
+
|
16
|
+
|
17
|
+
While I was writing the course I dreamed of a system where the docs and the
|
18
|
+
code could automatically stay in sync. One where if I had a typo in my tutorials, an automatic tech-editor would know and tell me. One where I couldn't accidentally skip over critical sections leaving true novices confused.
|
19
|
+
|
20
|
+
Dream no more, because rundoc does just that:
|
21
|
+
|
22
|
+
Write docs, build software.
|
23
|
+
|
24
|
+
## Isn't this Overkill?
|
25
|
+
|
26
|
+
No. Many new doc writers skip steps accidentally, or omit lines of code with `...` and assume their readers can follow along. Even if this is true for 80% of your users, 20% of people will become frustrated and may give up as a result. I found by including [check steps](http://schneems.com/post/60359275700/prepare-do-test-make-your-technical-writing-shine) such as running `ls` to ensure directory contents were the difference between good docs and great ones. The only problem: the output of `ls` on a Rails 4.0.0 and 4.0.1 project may be different. So the only way to ensure output is to actually run the command and copy it into your docs. With rundoc you don't need to do that. Rundoc runs the command then it can insert the output for you.
|
27
|
+
|
28
|
+
If you don't intend on updating or revising your content, then this project is overkill. On the other hand if you're writing docs without the intent of revising them, you probably shouldn't be writing technical docs.
|
29
|
+
|
30
|
+
## Install
|
31
|
+
|
32
|
+
This software is distributed as a Rubygem. Install it manually:
|
33
|
+
|
34
|
+
```
|
35
|
+
$ gem install rundoc
|
36
|
+
```
|
37
|
+
|
38
|
+
or add it to your Gemfile:
|
39
|
+
|
40
|
+
```
|
41
|
+
gem 'rundoc`
|
42
|
+
```
|
43
|
+
|
44
|
+
## Use It
|
45
|
+
|
46
|
+
Run the `rundoc build` command on any markdown file
|
47
|
+
|
48
|
+
```sh
|
49
|
+
$ rundoc build --path rundoc.md
|
50
|
+
```
|
51
|
+
|
52
|
+
Note: This command will create and manipulate directories in the working directory of your source markdown file. Best practice is to have your source markdown file in it's own empty directory.
|
53
|
+
|
54
|
+
This will generate a project folder with your project in it, and a markdown README.md with the parsed output of the markdown docs, and a copy of the source.
|
55
|
+
|
56
|
+
## Write it:
|
57
|
+
|
58
|
+
Rundoc uses github flavored markdown. This means you write like normal but in your code sections
|
59
|
+
you can add special annotations that when run through rundoc can
|
60
|
+
generate a project.
|
61
|
+
|
62
|
+
All rundoc commands are prefixed with three colons `:::` and are inclosed in a code block a
|
63
|
+
command such as `$` which is an alias for `bash` commands like this:
|
64
|
+
|
65
|
+
```
|
66
|
+
::: $ git init .
|
67
|
+
```
|
68
|
+
|
69
|
+
Nothing before the three colons matters. The space between the colons
|
70
|
+
and the command is optional.
|
71
|
+
|
72
|
+
|
73
|
+
If you don't want the command to output to your markdown document you
|
74
|
+
can add a minus symbol `-` to the end to prevent it from being
|
75
|
+
rendered.
|
76
|
+
|
77
|
+
```
|
78
|
+
:::- $ git init .
|
79
|
+
```
|
80
|
+
|
81
|
+
Note: If all commands inside of a code block are hidden, the entire codeblock will not be rendered.
|
82
|
+
|
83
|
+
If you want the output of the actual command to be rendered to
|
84
|
+
the screen you can use an equal sign so that
|
85
|
+
|
86
|
+
```
|
87
|
+
:::= $ ls
|
88
|
+
```
|
89
|
+
|
90
|
+
This code block might generate an output something like this to your markdown doc:
|
91
|
+
|
92
|
+
```
|
93
|
+
$ ls
|
94
|
+
Gemfile README.rdoc app config.ru doc log script tmp
|
95
|
+
Gemfile.lock Rakefile config db lib public test vendor
|
96
|
+
```
|
97
|
+
|
98
|
+
That's the syntax, let's look at different rundoc commands
|
99
|
+
|
100
|
+
## Shell Commands
|
101
|
+
|
102
|
+
Current Commands:
|
103
|
+
|
104
|
+
- `$`
|
105
|
+
- `fail.$
|
106
|
+
|
107
|
+
Anything you pass to `$` will be run in a shell. Any items below the command will be passed into the stdin of the bash command so:
|
108
|
+
|
109
|
+
```
|
110
|
+
:::= $ tail -n 2
|
111
|
+
foo
|
112
|
+
bar
|
113
|
+
baz
|
114
|
+
bahz
|
115
|
+
```
|
116
|
+
|
117
|
+
Would output:
|
118
|
+
|
119
|
+
```
|
120
|
+
$ tail -n 2
|
121
|
+
baz
|
122
|
+
bahz
|
123
|
+
```
|
124
|
+
|
125
|
+
This could be useful if you are running an interactive command such as `play new` which requires user input. For more fine grained input you'll need to use a custom repl object (will be covered later).
|
126
|
+
|
127
|
+
If a shell command returns a non-zero exit status an error will be raised, if you expect a command to fail you can run it with `fail.$` keyword
|
128
|
+
|
129
|
+
```
|
130
|
+
:::= fail.$ cat /dev/null/foo
|
131
|
+
```
|
132
|
+
|
133
|
+
Even though this command returns a non zero exit status, the contents of the command will be written since we're stating that we don't care if the command fails. This would be the output:
|
134
|
+
|
135
|
+
|
136
|
+
```
|
137
|
+
$ cat /dev/null/foo
|
138
|
+
cat: /dev/null/foo: Not a directory
|
139
|
+
```
|
140
|
+
|
141
|
+
Some commands may be custom, for example when running `cd` you likely want to change the working directory that your script is running in. To do this we need to run `Dir.chdir` instead of shelling out. So this works as you would expect:
|
142
|
+
|
143
|
+
|
144
|
+
```
|
145
|
+
:::= $ cd myapp/config
|
146
|
+
:::= $ cat database.yml
|
147
|
+
```
|
148
|
+
|
149
|
+
However this command would fall on it's face:
|
150
|
+
|
151
|
+
```
|
152
|
+
:::= $ cd myapp/config && cat database.yml
|
153
|
+
```
|
154
|
+
|
155
|
+
These custom commands are kept to a minimum, and for the most part behave as you would expect them to. Write your docs as you normally would and check the output frequently.
|
156
|
+
|
157
|
+
Running shell commands like this can be very powerful, you'll likely want more control of how you manipulate files in your project. To do this you can use the `file.` namespace:
|
158
|
+
|
159
|
+
## File Commands
|
160
|
+
|
161
|
+
Current Commands:
|
162
|
+
|
163
|
+
- `file.write`
|
164
|
+
- `file.append`
|
165
|
+
- `file.remove`
|
166
|
+
|
167
|
+
Use the `filewrite` keyword followed by a filename, on the next line(s) put the contents of the file
|
168
|
+
|
169
|
+
```
|
170
|
+
::: file.write config/routes.rb
|
171
|
+
|
172
|
+
Example::Application.routes.draw do
|
173
|
+
root :to => "pages#index"
|
174
|
+
|
175
|
+
namespace :users do
|
176
|
+
resources :after_signup
|
177
|
+
end
|
178
|
+
end
|
179
|
+
```
|
180
|
+
|
181
|
+
If you wanted to change `users` to `products` you could write to the same file again.
|
182
|
+
|
183
|
+
```
|
184
|
+
::: file.write config/routes.rb
|
185
|
+
Example::Application.routes.draw do
|
186
|
+
root :to => "pages#index"
|
187
|
+
|
188
|
+
namespace :products do
|
189
|
+
resources :after_signup
|
190
|
+
end
|
191
|
+
end
|
192
|
+
```
|
193
|
+
|
194
|
+
To fully delete files use bash `$` command such as `::: $ rm foo.rb`.
|
195
|
+
|
196
|
+
To add contents to a file you can use `file.append`
|
197
|
+
|
198
|
+
```
|
199
|
+
:::= file.append myapp/Gemfile
|
200
|
+
gem 'pg'
|
201
|
+
gem 'sextant', group: :development
|
202
|
+
gem 'wicked'
|
203
|
+
gem 'opro'
|
204
|
+
```
|
205
|
+
|
206
|
+
The contents of the file (in this example a file named `Gemfile`) will remain unchanged, but the contents of the `file.append` block will now appear in the bottom of the file. If you want to append the contents to a specific part of the file instead of the end of the file you can specify line number by putting a hash (`#`) then a number following it.
|
207
|
+
|
208
|
+
```
|
209
|
+
:::= file.append myapp/Gemfile#22
|
210
|
+
gem 'rails_12factor'
|
211
|
+
```
|
212
|
+
This will add the `gem 'rails_12factor'` on line 22 of the file `myapp/Gemfile`. If line 22 has existing contents, they will be bumped down to line 23.
|
213
|
+
|
214
|
+
Some times you may want to remove a small amount of text from an existing file. You can do this using `file.remove`, you pass in the contents you want removed:
|
215
|
+
|
216
|
+
```
|
217
|
+
:::= file.remove myapp/Gemfile
|
218
|
+
gem 'sqlite3'
|
219
|
+
```
|
220
|
+
|
221
|
+
When this is run, the file `Gemfile` will be modified to not include `gem 'sqlite3'`.
|
222
|
+
|
223
|
+
Note: `file.remove` currently requires a very explicit match so things like double versus single quotes, whitespace, and letter case all matter. Current best practice is to only use it for single line removals.
|
224
|
+
|
225
|
+
|
226
|
+
## Pipe
|
227
|
+
|
228
|
+
Commands:
|
229
|
+
- `|`
|
230
|
+
- `pipe` (aliased `|`)
|
231
|
+
|
232
|
+
Sometimes you need to need to pass data from one command to another. To do this there is a provided pipe command `|`
|
233
|
+
|
234
|
+
Let's say you want to output the first 23 lines of a file but you don't want to confuse your users with an additional pipe command in your shell line you could write something like this:
|
235
|
+
|
236
|
+
```sh
|
237
|
+
::: $ cat config/database.yml
|
238
|
+
:::= | $ head -n 23
|
239
|
+
```
|
240
|
+
|
241
|
+
Anything after the pipe `|` will generate a new command with the output of the previous command passed to it. The pipe command will only ouput its result, so the user will not know it was even executed.
|
242
|
+
|
243
|
+
This command is currently hacked together, and needs a refactor. Use it, but if something does not behave as you would expected open an issue and explain it.
|
244
|
+
|
245
|
+
|
246
|
+
## Configure
|
247
|
+
|
248
|
+
You can configure your docs in your docs use the `rundoc` command
|
249
|
+
|
250
|
+
```
|
251
|
+
:::- rundoc
|
252
|
+
```
|
253
|
+
|
254
|
+
Note: Make sure you run this as a hidden command (with `-`).
|
255
|
+
|
256
|
+
** After Build**
|
257
|
+
|
258
|
+
This will eval any code you put under that line (in Ruby). If you want to run some code after you're done building your docs you could use `Rundoc.configure` block and call the `after_build` method like this:
|
259
|
+
|
260
|
+
|
261
|
+
```
|
262
|
+
:::- rundoc
|
263
|
+
Rundoc.configure do |config|
|
264
|
+
config.after_build do
|
265
|
+
puts "you could push to github here"
|
266
|
+
puts "You could do anything here"
|
267
|
+
puts "This code will run after the docs are done building"
|
268
|
+
end
|
269
|
+
end
|
270
|
+
```
|
271
|
+
|
272
|
+
|
273
|
+
**Project Root**
|
274
|
+
|
275
|
+
By default your app builds in a `tmp` directory. If any failures occur the results will remain in `tmp`. On a successful build the contents are copied over to `project`. If you are generating a new rails project in your code `$ rails new myapp`. Then the finished directory would be in `project/myapp`. If you don't like the `./project` prefix you could tell rundoc to output contents in `./myapp` instead.
|
276
|
+
|
277
|
+
```
|
278
|
+
:::- rundoc
|
279
|
+
Rundoc.configure do |config|
|
280
|
+
config.project_root = "myapp"
|
281
|
+
end
|
282
|
+
```
|
283
|
+
|
284
|
+
This will also be the root directory that the `after_build` is executed in.
|
285
|
+
|
286
|
+
**Filter Sensitive Info**
|
287
|
+
|
288
|
+
Sometimes sensitive info like usernames, email addresses, or passwords may be introduced to the output readme. Let's say that your email address was `schneems@example.com` you could filter this out of your final document and replace it with `developer@example.com` instead like this:
|
289
|
+
|
290
|
+
```
|
291
|
+
:::- rundoc
|
292
|
+
Rundoc.configure do |config|
|
293
|
+
config.filter_sensitive("schneems@exmaple.com" => "developer@example.com")
|
294
|
+
end
|
295
|
+
```
|
296
|
+
|
297
|
+
This command `filter_sensitive` can be called multiple times with different values. Since the config is in Ruby you could iterate over an array of sensitive data
|
298
|
+
|
299
|
+
## TODO
|
300
|
+
|
301
|
+
This is a section for brainstorming. If it's here it's not guaranteed to get worked on, but it will be considered.
|
302
|
+
|
303
|
+
- Breakpoints?
|
304
|
+
- Better line matching for backtrace
|
305
|
+
- `-=` command (runs command, only shows output, does not show command)
|
306
|
+
- A way to run background processes indefinitely such as `rails server`
|
307
|
+
- An easy test syntax?
|
308
|
+
- Screenshot tool(s) ?!?!?!?!?!?! :)
|
309
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
|
4
|
+
require 'rundoc'
|
5
|
+
|
6
|
+
require 'rake'
|
7
|
+
require 'rake/testtask'
|
8
|
+
|
9
|
+
task :default => [:test]
|
10
|
+
|
11
|
+
test_task = Rake::TestTask.new(:test) do |t|
|
12
|
+
t.libs << 'lib'
|
13
|
+
t.libs << 'test'
|
14
|
+
t.pattern = 'test/rundoc/**/*_test.rb'
|
15
|
+
t.verbose = false
|
16
|
+
end
|
data/bin/rundoc
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$stdout.sync = true
|
4
|
+
|
5
|
+
unless File.respond_to? :realpath
|
6
|
+
class File #:nodoc:
|
7
|
+
def self.realpath path
|
8
|
+
return realpath(File.readlink(path)) if symlink?(path)
|
9
|
+
path
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
$: << File.expand_path(File.dirname(File.realpath(__FILE__)) + '/../lib')
|
14
|
+
|
15
|
+
require 'rundoc'
|
16
|
+
require 'thor'
|
17
|
+
|
18
|
+
class RundocCLI < Thor
|
19
|
+
|
20
|
+
def initialize(*args)
|
21
|
+
super
|
22
|
+
@path = options[:path]
|
23
|
+
@working_dir = Pathname.new(File.expand_path("../", @path))
|
24
|
+
end
|
25
|
+
|
26
|
+
default_task :help
|
27
|
+
|
28
|
+
desc "build", "turns rundoc file into docs and a project"
|
29
|
+
class_option :path, banner: "path/to/file.md", optional: true, default: 'rundoc.md'
|
30
|
+
def build
|
31
|
+
raise "#{@path} does not exist" unless File.exist?(@path)
|
32
|
+
source_contents = File.read(@path)
|
33
|
+
tmp_dir = @working_dir.join("tmp")
|
34
|
+
|
35
|
+
FileUtils.remove_entry_secure(tmp_dir) if tmp_dir.exist?
|
36
|
+
tmp_dir.mkdir
|
37
|
+
|
38
|
+
puts "== Running your docs"
|
39
|
+
Dir.chdir(tmp_dir) do
|
40
|
+
@output = Rundoc::Parser.new(source_contents, Rundoc.parser_options).to_md
|
41
|
+
Rundoc.sanitize(@output)
|
42
|
+
banner = "<!-- STOP\nThis file was generated by a rundoc script, don't modify it. \n"
|
43
|
+
banner << "Instead modify the rundoc script and re-run it.\nSTOP -->"
|
44
|
+
@output = "#{banner}\n#{@output}"
|
45
|
+
end
|
46
|
+
|
47
|
+
puts "== Done, run was successful"
|
48
|
+
project_name = if Rundoc.project_root
|
49
|
+
Rundoc.project_root.split('/').last
|
50
|
+
else
|
51
|
+
'project'
|
52
|
+
end
|
53
|
+
|
54
|
+
project_dir = @working_dir.join(project_name)
|
55
|
+
|
56
|
+
FileUtils.remove_entry_secure(project_dir) if project_dir.exist?
|
57
|
+
|
58
|
+
cp_root = if Rundoc.project_root
|
59
|
+
tmp_dir.join(Rundoc.project_root, ".")
|
60
|
+
else
|
61
|
+
tmp_dir.join(".")
|
62
|
+
end
|
63
|
+
|
64
|
+
FileUtils.cp_r(cp_root, project_dir)
|
65
|
+
|
66
|
+
FileUtils.remove_entry_secure(tmp_dir) if tmp_dir.exist?
|
67
|
+
|
68
|
+
puts "== Done, writing original source to #{project_dir}/source.md"
|
69
|
+
source_path = project_dir.join("README.md")
|
70
|
+
File.open(source_path, "w") { |f| f.write @output }
|
71
|
+
|
72
|
+
puts "== Copying source"
|
73
|
+
source_path = project_dir.join("coppied-#{@path.split('/').last}")
|
74
|
+
File.open(source_path, "w") { |f| f.write source_contents }
|
75
|
+
|
76
|
+
Dir.chdir(project_dir) do
|
77
|
+
Rundoc.run_after_build
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
RundocCLI.start(ARGV)
|