document_generator 0.0.2

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.
@@ -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