document_generator 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'document_generator/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'document_generator'
8
+ spec.version = DocumentGenerator::VERSION
9
+ spec.authors = ['wiscoDude', 'm5rk', 'stevenhallen']
10
+ spec.email = ['philip@stevenhallen.com', 'mark@stevenhallen.com']
11
+ spec.description = <<-DOCUMENT_GENERATOR
12
+ Generate documentation from a git repository.
13
+ DOCUMENT_GENERATOR
14
+ spec.summary = 'Generate documentation from a git repository.'
15
+ spec.homepage = 'http://github.com/stevenhallen/document_generator'
16
+ spec.license = 'MIT'
17
+
18
+ spec.files = `git ls-files`.split($/)
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.add_runtime_dependency 'addressable', '~> 2.3'
24
+ spec.add_runtime_dependency 'git', '~> 1.2'
25
+
26
+ spec.add_development_dependency 'bundler', '~> 1.3'
27
+ spec.add_development_dependency 'rake'
28
+ spec.add_development_dependency 'rspec', '~> 2.14'
29
+ spec.add_development_dependency 'rspec-fire', '~> 1.3'
30
+ spec.add_development_dependency 'simplecov', '~> 0.7'
31
+ end
data/index.md ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ layout: default
3
+ title: Home
4
+ ---
5
+ #{% include name.md %}
6
+
7
+ TODO: We could put the content of the README file here, right?
@@ -0,0 +1,17 @@
1
+ require 'addressable/uri'
2
+ require 'git'
3
+ require 'optparse'
4
+ require 'ostruct'
5
+ require 'tmpdir'
6
+ require 'uri'
7
+
8
+ require 'document_generator/version'
9
+
10
+ require 'document_generator/cli'
11
+ require 'document_generator/commit'
12
+ require 'document_generator/diff_file'
13
+ require 'document_generator/output'
14
+ require 'document_generator/repository'
15
+
16
+ module DocumentGenerator
17
+ end
@@ -0,0 +1,27 @@
1
+ module DocumentGenerator
2
+ class CLI
3
+ def self.start(args)
4
+ options = parse(args)
5
+
6
+ Repository.new(options.url).generate
7
+ end
8
+
9
+ def self.parse(args)
10
+ options = OpenStruct.new
11
+
12
+ parser = OptionParser.new do |opts|
13
+ opts.on('-u', '--url URL',
14
+ 'URL for the repository') do |url|
15
+ options.url = url
16
+ end
17
+ end
18
+
19
+ parser.parse!(args)
20
+
21
+ # TODO: Do something better than this.
22
+ raise OptionParser::MissingArgument unless options.url
23
+
24
+ options
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,85 @@
1
+ module DocumentGenerator
2
+ class Commit
3
+ attr_accessor :base_url, :git_commit
4
+
5
+ def initialize(base_url, git_commit)
6
+ @base_url = base_url
7
+ @git_commit = git_commit
8
+ end
9
+
10
+ def diff_files
11
+ return [] unless git_commit.parent
12
+
13
+ git_commit.parent.diff(git_commit).map do |git_diff_file|
14
+ DiffFile.new(git_diff_file)
15
+ end
16
+ end
17
+
18
+ def relative_filename
19
+ filename
20
+ end
21
+
22
+ def create
23
+ File.open(relative_filename, 'w') do |writer|
24
+ writer.write(header)
25
+ writer.write(details_of_commit_message) if details_of_commit_message
26
+
27
+ diff_files.each do |diff_file|
28
+ writer.write(diff_file.content)
29
+ end
30
+
31
+ writer.write(additional)
32
+ end
33
+ end
34
+
35
+ def header
36
+ <<-HEADER
37
+ ---
38
+ layout: default
39
+ title: #{first_line_of_commit_message}
40
+ ---
41
+
42
+ <h1 id="main">#{first_line_of_commit_message}</h1>
43
+ HEADER
44
+ end
45
+
46
+ def additional
47
+ <<-ADDITIONAL
48
+
49
+ ### Additional Resources
50
+
51
+ * [Changes in this step in `diff` format](#{URI.join(base_url, 'commit/', git_commit.sha)})
52
+
53
+ ADDITIONAL
54
+ end
55
+
56
+ def commit_message_lines
57
+ git_commit.message.split("\n")
58
+ end
59
+
60
+ def first_line_of_commit_message
61
+ commit_message_lines.first
62
+ end
63
+
64
+ def details_of_commit_message
65
+ commit_message_lines[1..-1].join("\n") if commit_message_lines.length > 1
66
+ end
67
+
68
+ def basename_prefix
69
+ message = first_line_of_commit_message
70
+ message = message.split.join('-')
71
+ message.gsub!(%r{[^\w-]}, '')
72
+ message.downcase!
73
+ message.tr!('_', '-')
74
+ message
75
+ end
76
+
77
+ def filename
78
+ "#{basename_prefix}.md"
79
+ end
80
+
81
+ def link
82
+ "<li><a href='#{basename_prefix}.html'>#{first_line_of_commit_message}</a></li>"
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,157 @@
1
+ require 'cgi'
2
+
3
+ module DocumentGenerator
4
+ class DiffFile
5
+ attr_accessor :git_diff_file
6
+
7
+ def type
8
+ git_diff_file.type
9
+ end
10
+
11
+ def initialize(git_diff_file)
12
+ @git_diff_file = git_diff_file
13
+ end
14
+
15
+ def git_diff_file_lines
16
+ git_diff_file.patch.split("\n")
17
+ end
18
+
19
+ def patch_heading
20
+ "#{action_type} `#{git_diff_file.path}`"
21
+ end
22
+
23
+ def content
24
+ if type == 'deleted'
25
+ return patch_heading + "\n\n"
26
+ end
27
+
28
+ temp = []
29
+ temp << patch_heading
30
+
31
+ if markdown_outputs.any?
32
+ markdown_outputs.each do |output|
33
+ temp << "\n\n"
34
+ temp << output.description
35
+ temp << "\n<pre><code>"
36
+ temp << output.escaped_content
37
+ temp << "</code></pre>\n"
38
+ end
39
+ end
40
+
41
+ if git_diff_file.type == "modified"
42
+ temp << "\n\n"
43
+ temp << "Becomes"
44
+ temp << "\n<pre><code>"
45
+ temp << ending_code
46
+ temp << "\n</code></pre>\n"
47
+ end
48
+
49
+ temp << "\n\n"
50
+
51
+ temp.join
52
+ end
53
+
54
+ def ending_code
55
+ clean_lines = []
56
+ git_diff_file_lines[code_line_start..-1].each_with_index do |line, index|
57
+
58
+ if (line[0]) == "-" || ignore_line?(line)
59
+ next
60
+ end
61
+
62
+ if (line[0]) == "+"
63
+ line = remove_first_character(line)
64
+ end
65
+ clean_lines << line
66
+ end
67
+ Output.no_really_escape(CGI.escapeHTML(clean_lines.join("\n")))
68
+ end
69
+
70
+ def action_type
71
+ { new: 'Create file',
72
+ modified: 'Update file',
73
+ deleted: 'Remove file' }.fetch(type.to_sym, type)
74
+ end
75
+
76
+ def markdown_outputs # returns an array of outputs
77
+ outputs = []
78
+ last_line = 0
79
+ git_diff_file_lines.each_with_index do |line, index|
80
+ next if index < code_line_start
81
+ next if index <= last_line
82
+ case line.strip[0]
83
+
84
+ when "+"
85
+ last_line = last_same_line(index)
86
+ output = Output.new
87
+ output.description = "Add"
88
+ output.content = line_block(index, last_line)
89
+ outputs << output
90
+ when "-"
91
+ if line_sign(index + 1) == "+"
92
+ output = Output.new
93
+ output.description = "Change"
94
+ output.content = line_block(index, last_same_line(index))
95
+ outputs << output
96
+ last_line = last_same_line(last_same_line(index) + 1)
97
+ output = Output.new
98
+ output.description = "To"
99
+ output.content = line_block(last_same_line(index) + 1, last_line)
100
+ outputs << output
101
+ else
102
+ output = Output.new
103
+ output.description = "Remove"
104
+ last_line = last_same_line(index)
105
+ output.content = line_block(index, last_line)
106
+ outputs << output
107
+ end
108
+ end
109
+
110
+ end
111
+ outputs
112
+ end
113
+
114
+ private
115
+
116
+ def ignore_line?(line)
117
+ line.strip == 'No newline at end of file'
118
+ end
119
+
120
+ def last_same_line(line_index)
121
+ starting_sign = line_sign(line_index)
122
+
123
+ git_diff_file_lines[line_index..-1].each_with_index do |line, index|
124
+ if line_sign(index + 1 + line_index) != starting_sign
125
+ return (index + line_index)
126
+ end
127
+ end
128
+ end
129
+
130
+ def line_block(beginning, ending)
131
+ lines = []
132
+ git_diff_file_lines[beginning..ending].each do |line|
133
+ if ["+", "-"].include?(line[0..0])
134
+ line = remove_first_character(line)
135
+ end
136
+ if !ignore_line?(line)
137
+ lines << line
138
+ end
139
+ end
140
+ lines
141
+ end
142
+
143
+ def line_sign(line_number)
144
+ (git_diff_file_lines[line_number] || '').strip[0]
145
+ end
146
+
147
+ def remove_first_character(line)
148
+ " " + line[1..-1]
149
+ end
150
+
151
+ def code_line_start
152
+ git_diff_file_lines.each_with_index do |line, index|
153
+ return (index + 1) if line[0..1] == "@@"
154
+ end
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,27 @@
1
+ module DocumentGenerator
2
+ class Output
3
+ attr_accessor :description, :content
4
+
5
+ # TODO: This is due to a bug in maruku. We should create
6
+ # an issue there--and possibly a PR to fix?
7
+ def self.no_really_escape(value)
8
+ value.split("\n").map do |line|
9
+ if line.strip.size.zero?
10
+ "&nbsp;"
11
+ else
12
+ line
13
+ end
14
+ end.join("\n")
15
+ end
16
+
17
+ def escaped_content
18
+ temp = []
19
+ content.each do |line|
20
+ temp << line
21
+ temp << "\n"
22
+ end
23
+
24
+ Output.no_really_escape(CGI.escapeHTML(temp.join.rstrip))
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,89 @@
1
+ module DocumentGenerator
2
+ class Repository
3
+ attr_accessor :url
4
+
5
+ def self.menu_dirname
6
+ '_includes'
7
+ end
8
+
9
+ def self.menu_relative_filename
10
+ File.join(menu_dirname, 'menu.md')
11
+ end
12
+
13
+ def self.default_dirname
14
+ '_layouts'
15
+ end
16
+
17
+ def self.default_relative_filename
18
+ File.join(default_dirname, 'default.html')
19
+ end
20
+
21
+ def initialize(url)
22
+ @url = url
23
+ end
24
+
25
+ def base_url
26
+ "https://#{uri.host}#{uri.path}/"
27
+ end
28
+
29
+ def name
30
+ uri.path.split('/')[-1]
31
+ end
32
+
33
+ def generate
34
+ prepare
35
+
36
+ File.open(Repository.menu_relative_filename, 'w') do |menu_writer|
37
+ commits do |commit|
38
+ menu_writer.write(commit.link)
39
+ commit.create
40
+ end
41
+ end
42
+ end
43
+
44
+ def commits
45
+ Dir.mktmpdir do |path|
46
+ repo = Git.clone(url, name, path: path)
47
+
48
+ # TODO: Allow options to influence branch, number of commits, etc.
49
+ repo.log(nil).reverse_each.map do |git_commit|
50
+ yield Commit.new(base_url, git_commit)
51
+ end
52
+ end
53
+ end
54
+
55
+ private
56
+ def prepare
57
+ Dir.mkdir(Repository.menu_dirname) unless Dir.exists?(Repository.menu_dirname)
58
+ copy_layout
59
+ end
60
+
61
+ def copy_layout
62
+ return if File.exists?(Repository.default_relative_filename)
63
+
64
+ Dir.mkdir(Repository.default_dirname) unless Dir.exists?(Repository.default_dirname)
65
+
66
+ src = File.expand_path('../../../assets/_layouts/default.html', __FILE__)
67
+ dest = Repository.default_relative_filename
68
+
69
+ FileUtils.copy_file(src, dest)
70
+ end
71
+
72
+ def normalized_url
73
+ replacements = [
74
+ [%r(\Agit@github\.com:), 'git://github.com/'],
75
+ [%r(\.git\Z), '']
76
+ ]
77
+
78
+ replacements.each do |pattern, replacement|
79
+ url.gsub!(pattern, replacement)
80
+ end
81
+
82
+ url
83
+ end
84
+
85
+ def uri
86
+ Addressable::URI.parse(normalized_url)
87
+ end
88
+ end
89
+ end