rundoc 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,5 @@
1
+ test/fixtures/*/project/
2
+ test/fixtures/*/tmp/
3
+ test/fixtures/*/myapp/
4
+ *.gem
5
+
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
@@ -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!
@@ -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
+
@@ -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
@@ -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)