bookingit 0.0.1 → 0.1.0
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +4 -0
- data/Gemfile.lock +7 -1
- data/README.rdoc +86 -37
- data/TODO.md +7 -0
- data/bin/bookingit +19 -58
- data/bookingit.gemspec +2 -0
- data/features/bookingit.feature +149 -16
- data/features/step_definitions/bookinggit_steps.rb +18 -0
- data/features/support/env.rb +5 -1
- data/lib/bookingit.rb +5 -0
- data/lib/bookingit/book.rb +94 -0
- data/lib/bookingit/code_block_interpreter.rb +114 -0
- data/lib/bookingit/config.rb +62 -28
- data/lib/bookingit/errors.rb +16 -0
- data/lib/bookingit/renderer.rb +173 -39
- data/lib/bookingit/shell_command.rb +30 -0
- data/lib/bookingit/version.rb +1 -1
- data/lib/bookingit/views.rb +8 -0
- data/lib/bookingit/views/base_view.rb +12 -0
- data/lib/bookingit/views/code_view.rb +16 -0
- data/lib/bookingit/views/footer_view.rb +14 -0
- data/lib/bookingit/views/header_view.rb +17 -0
- data/lib/bookingit/views/index_view.rb +20 -0
- data/templates/block_code.html.mustache +1 -0
- data/templates/footer.html.mustache +37 -0
- data/templates/header.html.mustache +16 -0
- data/templates/index.html.mustache +27 -0
- data/templates/stylesheets.html.mustache +3 -0
- data/templates/syntax_highlighting.html.mustache +3 -0
- data/test/book_test.rb +20 -0
- data/test/config_test.rb +43 -35
- data/test/renderer_test.rb +173 -23
- data/test/test_helper.rb +2 -0
- metadata +50 -2
|
@@ -24,3 +24,21 @@ Given(/^this config file:$/) do |string|
|
|
|
24
24
|
end
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
Given(/^a git repo "(.*?)" in "(.*?)" containing the file "(.*?)" and a tag "(.*?)"$/) do |repo_name, repo_basedir, file_name, tag_name|
|
|
28
|
+
FileUtils.chdir "tmp/aruba" do
|
|
29
|
+
FileUtils.mkdir repo_basedir
|
|
30
|
+
@dirs_created << repo_basedir
|
|
31
|
+
FileUtils.chdir repo_basedir do
|
|
32
|
+
FileUtils.mkdir repo_name
|
|
33
|
+
FileUtils.chdir repo_name do
|
|
34
|
+
File.open(file_name,'w') do |file|
|
|
35
|
+
file.puts "Some stuff and whatnot"
|
|
36
|
+
end
|
|
37
|
+
system "git init"
|
|
38
|
+
system "git add #{file_name}"
|
|
39
|
+
system "git commit -m 'initial'"
|
|
40
|
+
system "git tag #{tag_name}"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
data/features/support/env.rb
CHANGED
|
@@ -8,12 +8,16 @@ Before do
|
|
|
8
8
|
@puts = true
|
|
9
9
|
@original_rubylib = ENV['RUBYLIB']
|
|
10
10
|
@files_created = []
|
|
11
|
+
@dirs_created = []
|
|
11
12
|
ENV['RUBYLIB'] = LIB_DIR + File::PATH_SEPARATOR + ENV['RUBYLIB'].to_s
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
After do
|
|
15
16
|
ENV['RUBYLIB'] = @original_rubylib
|
|
16
17
|
@files_created.each do |file|
|
|
17
|
-
FileUtils.rm file
|
|
18
|
+
FileUtils.rm file if File.exists?(file)
|
|
19
|
+
end
|
|
20
|
+
@dirs_created.each do |dir|
|
|
21
|
+
FileUtils.rm_rf dir if File.exists?(dir)
|
|
18
22
|
end
|
|
19
23
|
end
|
data/lib/bookingit.rb
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
require 'bookingit/views'
|
|
1
2
|
require 'bookingit/version.rb'
|
|
3
|
+
require 'bookingit/errors.rb'
|
|
4
|
+
require 'bookingit/code_block_interpreter.rb'
|
|
5
|
+
require 'bookingit/shell_command.rb'
|
|
2
6
|
require 'bookingit/renderer.rb'
|
|
3
7
|
require 'bookingit/config.rb'
|
|
8
|
+
require 'bookingit/book.rb'
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
module Bookingit
|
|
2
|
+
class Book
|
|
3
|
+
def initialize(config)
|
|
4
|
+
@config = config
|
|
5
|
+
@output_dir = 'book'
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def render_html!
|
|
9
|
+
mkdir @output_dir unless Dir.exists?(@output_dir)
|
|
10
|
+
|
|
11
|
+
rendering_config = @config.rendering_config
|
|
12
|
+
rendering_config[:cache] = File.expand_path('cache') if @config.cache
|
|
13
|
+
renderer = Bookingit::Renderer.new(@config)
|
|
14
|
+
@redcarpet = Redcarpet::Markdown.new(renderer, no_intra_emphasis: true,
|
|
15
|
+
tables: true,
|
|
16
|
+
fenced_code_blocks: true,
|
|
17
|
+
autolink: true,
|
|
18
|
+
strikethrough: true,
|
|
19
|
+
superscript: true)
|
|
20
|
+
|
|
21
|
+
toc = parse_chapters_to_get_headers(renderer)
|
|
22
|
+
generate_chapters(renderer)
|
|
23
|
+
generate_toc(toc,renderer.stylesheets,renderer.theme)
|
|
24
|
+
copy_assets(renderer)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def parse_chapters_to_get_headers(renderer)
|
|
30
|
+
toc = {}
|
|
31
|
+
each_chapter do |matter,contents,chapter|
|
|
32
|
+
toc[matter] ||= []
|
|
33
|
+
renderer.current_chapter = chapter
|
|
34
|
+
@redcarpet.render(contents)
|
|
35
|
+
chapter.title = Array(renderer.headers[1]).first
|
|
36
|
+
toc[matter] << chapter
|
|
37
|
+
end
|
|
38
|
+
toc
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def each_chapter(&block)
|
|
42
|
+
%w(front_matter main_matter back_matter).each do |matter|
|
|
43
|
+
@config.send(matter).chapters.each do |chapter|
|
|
44
|
+
block.call(matter,File.read(chapter.markdown_path),chapter)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def generate_chapters(renderer)
|
|
50
|
+
each_chapter do |_,contents,chapter|
|
|
51
|
+
output_file = chapter.relative_url
|
|
52
|
+
renderer.current_chapter = chapter
|
|
53
|
+
File.open(File.join(@output_dir,output_file),'w') do |file|
|
|
54
|
+
file.puts @redcarpet.render(contents)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def generate_toc(toc,stylesheets,theme)
|
|
60
|
+
if @config.templates["index"] =~ /^\//
|
|
61
|
+
Views::IndexView.template_path = File.dirname(@config.templates["index"])
|
|
62
|
+
Views::IndexView.template_name = File.basename(@config.templates["index"])
|
|
63
|
+
else
|
|
64
|
+
Views::IndexView.template_name = @config.templates["index"]
|
|
65
|
+
end
|
|
66
|
+
view = Views::IndexView.new(stylesheets,
|
|
67
|
+
theme,
|
|
68
|
+
toc['front_matter'],
|
|
69
|
+
toc['main_matter'],
|
|
70
|
+
toc['back_matter'],
|
|
71
|
+
@config)
|
|
72
|
+
File.open(File.join(@output_dir,'index.html'),'w') do |index|
|
|
73
|
+
index.puts view.render
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def copy_assets(renderer)
|
|
78
|
+
@config.rendering_config[:stylesheets].each do |stylesheet|
|
|
79
|
+
cp stylesheet, @output_dir
|
|
80
|
+
end
|
|
81
|
+
renderer.images.each do |image|
|
|
82
|
+
if File.exists?(image)
|
|
83
|
+
dest_dir = File.join(@output_dir,File.dirname(image))
|
|
84
|
+
unless File.exists? dest_dir
|
|
85
|
+
mkdir_p dest_dir
|
|
86
|
+
end
|
|
87
|
+
cp image,dest_dir
|
|
88
|
+
else
|
|
89
|
+
$stderr.puts "Missing image #{image}"
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
module Bookingit
|
|
2
|
+
class CodeBlockInterpreter
|
|
3
|
+
def initialize(code)
|
|
4
|
+
@code = code.strip
|
|
5
|
+
@result = nil
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def when_file(&block)
|
|
9
|
+
if @code =~ /^\s*file:\/\/(.*)$/
|
|
10
|
+
path = $1
|
|
11
|
+
@result = block.call(path)
|
|
12
|
+
end
|
|
13
|
+
self
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def when_file_in_git(&block)
|
|
17
|
+
when_git_reference do |repo_path,path_in_repo,reference|
|
|
18
|
+
if reference !~ /^(.+)\.\.(.+)$/ && reference !~ /^(.+)\!(.+)$/ && reference !~ /^\.\.(.+)$/
|
|
19
|
+
chdir repo_path do
|
|
20
|
+
@result = block.call(path_in_repo,reference)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
self
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def when_git_diff(&block)
|
|
28
|
+
when_git_reference do |repo_path,path_in_repo,reference|
|
|
29
|
+
if reference =~ /^(.+)\.\.(.+)$/
|
|
30
|
+
chdir repo_path do
|
|
31
|
+
@result = block.call(path_in_repo,reference)
|
|
32
|
+
end
|
|
33
|
+
elsif reference =~ /^\.\.(.+)$/
|
|
34
|
+
tag_or_sha = $1
|
|
35
|
+
chdir repo_path do
|
|
36
|
+
@result = block.call(path_in_repo,"#{tag_or_sha}^..#{tag_or_sha}")
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
self
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def when_shell_command_in_git(&block)
|
|
44
|
+
when_git_reference do |repo_path,path_in_repo,reference|
|
|
45
|
+
if reference =~ /^([^!]+)\!(.+)$/
|
|
46
|
+
reference = $1
|
|
47
|
+
command,exit_type = parse_shell_command($2)
|
|
48
|
+
chdir repo_path do
|
|
49
|
+
@result = block.call(reference,shell_command(path: path_in_repo,command: command, exit_type: exit_type))
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def when_shell_command(&block)
|
|
56
|
+
if @code.strip =~ /^\s*sh:\/\/(.+)#([^#]+)$/
|
|
57
|
+
path = $1
|
|
58
|
+
command,exit_type = parse_shell_command($2)
|
|
59
|
+
@result = block.call(shell_command(path: path,command: command, exit_type: exit_type))
|
|
60
|
+
end
|
|
61
|
+
self
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def otherwise(&block)
|
|
65
|
+
@result = block.call if @result.nil?
|
|
66
|
+
self
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def result
|
|
70
|
+
raise "You didn't handle every possible case" if @result.nil?
|
|
71
|
+
@result
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
def shell_command(path: nil, command: nil, exit_type: nil)
|
|
77
|
+
ShellCommand.new(path: path,command: command) do |exit_status|
|
|
78
|
+
case exit_type
|
|
79
|
+
when :zero
|
|
80
|
+
exit_status == 0
|
|
81
|
+
when :nonzero
|
|
82
|
+
exit_status != 0
|
|
83
|
+
else
|
|
84
|
+
raise "unknown exit type #{exit_type}"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def parse_shell_command(shell_command)
|
|
90
|
+
if shell_command =~ /(^.*)!([^!]+)$/
|
|
91
|
+
[$1,$2.to_sym]
|
|
92
|
+
else
|
|
93
|
+
[shell_command,:zero]
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def when_git_reference(&block)
|
|
98
|
+
if @code =~ /^\s*git:\/\/(.*)$/
|
|
99
|
+
path = $1
|
|
100
|
+
if path =~ /(^.*).git\/(.*)#([^#]+)$/
|
|
101
|
+
repo_path = $1
|
|
102
|
+
path_in_repo = $2
|
|
103
|
+
path_in_repo = '.' if String(path_in_repo).strip == ''
|
|
104
|
+
reference = $3
|
|
105
|
+
block.call(repo_path,path_in_repo,reference)
|
|
106
|
+
else
|
|
107
|
+
raise "You must provide a SHA1 or tagname: #{path}"
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
self
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
end
|
|
114
|
+
end
|
data/lib/bookingit/config.rb
CHANGED
|
@@ -7,50 +7,84 @@ module Bookingit
|
|
|
7
7
|
|
|
8
8
|
attr_reader :front_matter,
|
|
9
9
|
:main_matter,
|
|
10
|
-
:back_matter
|
|
10
|
+
:back_matter,
|
|
11
|
+
:rendering_config,
|
|
12
|
+
:cache,
|
|
13
|
+
:options,
|
|
14
|
+
:templates
|
|
11
15
|
|
|
12
16
|
def initialize(config_json,root_dir)
|
|
13
17
|
config_hash = JSON.parse(config_json)
|
|
14
18
|
|
|
15
|
-
@front_matter
|
|
16
|
-
@main_matter
|
|
17
|
-
@back_matter
|
|
19
|
+
@front_matter = Matter.new(config_hash.delete('front_matter'),root_dir)
|
|
20
|
+
@main_matter = Matter.new(config_hash.delete('main_matter'),root_dir)
|
|
21
|
+
@back_matter = Matter.new(config_hash.delete('back_matter'),root_dir)
|
|
22
|
+
@templates = config_hash.delete("templates") || {}
|
|
23
|
+
@templates["index"] ||= "index.html"
|
|
24
|
+
@rendering_config = create_rendering_config(config_hash.delete('rendering'))
|
|
25
|
+
@cache = false
|
|
26
|
+
@options = config_hash
|
|
27
|
+
|
|
28
|
+
all_chapters = (@front_matter.chapters + @main_matter.chapters + @back_matter.chapters)
|
|
29
|
+
all_chapters.each_with_index do |chapter,i|
|
|
30
|
+
if i > 0
|
|
31
|
+
all_chapters[i-1].next_chapter = chapter
|
|
32
|
+
chapter.previous_chapter = all_chapters[i-1]
|
|
33
|
+
end
|
|
34
|
+
if i < (all_chapters.size-1)
|
|
35
|
+
all_chapters[i+1].previous_chapter = chapter
|
|
36
|
+
chapter.next_chapter = all_chapters[i+1]
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def cache=(cache)
|
|
42
|
+
@cache = cache
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def create_rendering_config(raw_config)
|
|
48
|
+
raw_config ||= {}
|
|
49
|
+
rendering_config = {}
|
|
50
|
+
rendering_config[:stylesheets] = Array(raw_config['stylesheets'])
|
|
51
|
+
rendering_config[:basedir] = raw_config['git_repos_basedir']
|
|
52
|
+
rendering_config[:languages] = Hash[(raw_config['languages'] || {}).map { |match,language|
|
|
53
|
+
if match =~ /^\/(.+)\/$/
|
|
54
|
+
[Regexp.new($1),language]
|
|
55
|
+
else
|
|
56
|
+
[match,language]
|
|
57
|
+
end
|
|
58
|
+
}]
|
|
59
|
+
rendering_config[:theme] = raw_config['syntax_theme']
|
|
60
|
+
|
|
61
|
+
rendering_config
|
|
18
62
|
end
|
|
19
63
|
|
|
20
64
|
class Matter
|
|
21
65
|
attr_reader :chapters
|
|
22
|
-
def initialize(
|
|
23
|
-
@chapters = Array(
|
|
24
|
-
Chapter.new(
|
|
66
|
+
def initialize(chapter_filenames,root_dir)
|
|
67
|
+
@chapters = Array(chapter_filenames).map { |chapter_filename|
|
|
68
|
+
Chapter.new(markdown_path: File.join(root_dir,chapter_filename))
|
|
25
69
|
}
|
|
26
70
|
end
|
|
27
71
|
end
|
|
28
72
|
|
|
29
73
|
class Chapter
|
|
30
|
-
attr_reader :sections
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def initialize(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if files.size == 1
|
|
38
|
-
@sections = []
|
|
39
|
-
@path = files[0]
|
|
40
|
-
else
|
|
41
|
-
@sections = files.map { |file|
|
|
42
|
-
Section.new(file)
|
|
43
|
-
}
|
|
44
|
-
end
|
|
74
|
+
attr_reader :markdown_path, :relative_url, :sections
|
|
75
|
+
attr_accessor :title, :previous_chapter, :next_chapter
|
|
76
|
+
|
|
77
|
+
def initialize(markdown_path: nil, relative_url: nil)
|
|
78
|
+
@markdown_path = markdown_path
|
|
79
|
+
@relative_url = relative_url || (File.basename(markdown_path, File.extname(markdown_path)) + ".html")
|
|
80
|
+
@sections = []
|
|
45
81
|
end
|
|
46
|
-
end
|
|
47
82
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
@
|
|
83
|
+
def add_section(title,anchor)
|
|
84
|
+
section = Chapter.new(relative_url: self.relative_url + "##{anchor}")
|
|
85
|
+
section.title = title
|
|
86
|
+
@sections << section
|
|
52
87
|
end
|
|
53
88
|
end
|
|
54
89
|
end
|
|
55
|
-
|
|
56
90
|
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'gli'
|
|
2
|
+
module Bookingit
|
|
3
|
+
class UnexpectedShellCommandExit < StandardError
|
|
4
|
+
include GLI::StandardException
|
|
5
|
+
attr_reader :command, :stdout, :stderr
|
|
6
|
+
def initialize(command,stdout,stderr)
|
|
7
|
+
@command = command
|
|
8
|
+
@stdout = stdout
|
|
9
|
+
@stderr = stderr
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def exit_code
|
|
13
|
+
126
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
data/lib/bookingit/renderer.rb
CHANGED
|
@@ -6,63 +6,197 @@ module Bookingit
|
|
|
6
6
|
class Renderer < Redcarpet::Render::HTML
|
|
7
7
|
include FileUtils
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
def initialize(config)
|
|
10
|
+
super()
|
|
11
|
+
options = config.rendering_config
|
|
12
|
+
additional_languages = Hash[(options[:languages] || {}).map { |ext_or_regexp,language|
|
|
13
|
+
if ext_or_regexp.kind_of? String
|
|
14
|
+
[/#{ext_or_regexp}$/,language]
|
|
15
|
+
else
|
|
16
|
+
[ext_or_regexp,language]
|
|
17
|
+
end
|
|
18
|
+
}]
|
|
19
|
+
@language_identifiers = EXTENSION_TO_LANGUAGE.merge(additional_languages)
|
|
20
|
+
@basedir = String(options[:basedir]).strip
|
|
21
|
+
@basedir = '.' if @basedir == ''
|
|
22
|
+
@stylesheets = Array(options[:stylesheets])
|
|
23
|
+
@theme = options[:theme] || "default"
|
|
24
|
+
@cachedir = options[:cache]
|
|
25
|
+
@config = config
|
|
26
|
+
@images = []
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
attr_accessor :headers, :stylesheets, :theme, :images
|
|
30
|
+
|
|
31
|
+
def current_chapter=(chapter)
|
|
32
|
+
@chapter = chapter
|
|
33
|
+
end
|
|
34
|
+
|
|
10
35
|
def header(text,header_level,anchor)
|
|
11
36
|
@headers[header_level] ||= []
|
|
12
37
|
@headers[header_level] << text
|
|
13
|
-
|
|
38
|
+
if header_level == 2
|
|
39
|
+
@chapter.add_section(text,anchor)
|
|
40
|
+
end
|
|
41
|
+
"<a name='#{anchor}'></a><h#{header_level+1}>#{text}</h#{header_level+1}>"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def image(link, title, alt_text)
|
|
45
|
+
title = title.gsub(/'/,'"') if title
|
|
46
|
+
@images << link
|
|
47
|
+
"<img src='#{link}' alt='#{alt_text}' title='#{title}'>"
|
|
14
48
|
end
|
|
15
49
|
|
|
16
50
|
def doc_header
|
|
17
51
|
@headers = {}
|
|
18
|
-
|
|
52
|
+
Views::HeaderView.new(@stylesheets,@theme,@config).render
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def doc_footer
|
|
56
|
+
Views::FooterView.new(@chapter,@config).render
|
|
19
57
|
end
|
|
20
58
|
|
|
21
59
|
EXTENSION_TO_LANGUAGE = {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
60
|
+
/\.rb$/ => 'ruby',
|
|
61
|
+
/\.html$/ => 'html',
|
|
62
|
+
/\.scala$/ => 'scala',
|
|
63
|
+
/Gemfile$/ => 'ruby',
|
|
25
64
|
}
|
|
65
|
+
|
|
66
|
+
def identify_language(path)
|
|
67
|
+
@language_identifiers.select { |matcher,language|
|
|
68
|
+
path =~ matcher
|
|
69
|
+
}.values.first
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
|
|
26
73
|
def block_code(code, language)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
language =
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
74
|
+
result = nil
|
|
75
|
+
filename = nil
|
|
76
|
+
chdir @basedir do
|
|
77
|
+
code,language,filename = CodeBlockInterpreter.new(code)
|
|
78
|
+
.when_file( &cache(:read_file))
|
|
79
|
+
.when_git_diff( &cache(:read_git_diff))
|
|
80
|
+
.when_shell_command_in_git(&cache(:run_shell_command_in_git))
|
|
81
|
+
.when_file_in_git( &cache(:read_file_in_git))
|
|
82
|
+
.when_shell_command( &cache(:run_shell_command))
|
|
83
|
+
.otherwise {
|
|
84
|
+
[code,language,nil]
|
|
85
|
+
}.result
|
|
86
|
+
end
|
|
87
|
+
Views::CodeView.new(code,filename,language,@config).render.strip
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def cache(method_name)
|
|
93
|
+
->(*args) {
|
|
94
|
+
if @cachedir && File.exist?(cached_filename(*args))
|
|
95
|
+
puts "Pulling from cache..."
|
|
96
|
+
lines = File.read(cached_filename(*args)).split(/\n/)
|
|
97
|
+
language = lines.shift
|
|
98
|
+
filename = lines.shift
|
|
99
|
+
[lines.join("\n") + "\n",language,filename]
|
|
100
|
+
else
|
|
101
|
+
code,language,filename = method(method_name).(*args)
|
|
102
|
+
if @cachedir
|
|
103
|
+
FileUtils.mkdir_p(@cachedir) unless File.exist?(@cachedir)
|
|
104
|
+
File.open(cached_filename(*args),'w') do |file|
|
|
105
|
+
file.puts language
|
|
106
|
+
file.puts filename
|
|
107
|
+
file.puts code
|
|
46
108
|
end
|
|
109
|
+
puts "Cached output"
|
|
47
110
|
end
|
|
111
|
+
[code,language,filename]
|
|
112
|
+
end
|
|
113
|
+
}
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def cached_filename(*args)
|
|
117
|
+
args = args.map { |arg|
|
|
118
|
+
case arg
|
|
119
|
+
when ShellCommand
|
|
120
|
+
[arg.command,arg.expected_exit_status].join("_")
|
|
48
121
|
else
|
|
49
|
-
|
|
122
|
+
arg
|
|
50
123
|
end
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
124
|
+
}
|
|
125
|
+
File.join(@cachedir,args.join('__').gsub(/[#\/\!\s><]/,'_'))
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def at_version_in_git(reference,&block)
|
|
129
|
+
ShellCommand.new(command: "git checkout #{reference} 2>&1").run!
|
|
130
|
+
block.call
|
|
131
|
+
ShellCommand.new(command: "git checkout master 2>&1").run!
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def capture_command_output(path,command,exit_type=:zero)
|
|
135
|
+
shell_command = ShellCommand.new(command: command,path: path) do |exit_status|
|
|
136
|
+
case exit_type
|
|
137
|
+
when :zero
|
|
138
|
+
exit_status == 0
|
|
139
|
+
when :nonzero
|
|
140
|
+
exit_status != 0
|
|
141
|
+
else
|
|
142
|
+
raise "unknown exit type #{exit_type}"
|
|
58
143
|
end
|
|
59
144
|
end
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
145
|
+
shell_command.run!
|
|
146
|
+
["> #{command}\n#{shell_command.stdout}",'shell']
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def read_file(path)
|
|
150
|
+
filename = path
|
|
151
|
+
[File.read(path),identify_language(path),filename]
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def read_git_diff(path_in_repo,reference)
|
|
155
|
+
puts "Calculating git diff #{reference}"
|
|
156
|
+
filename = path_in_repo
|
|
157
|
+
shell_command = ShellCommand.new(command: "git diff #{reference} #{path_in_repo}")
|
|
158
|
+
shell_command.run!
|
|
159
|
+
[ shell_command.stdout, 'diff', filename ]
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def run_shell_command_in_git(reference,shell_command)
|
|
163
|
+
code = nil
|
|
164
|
+
at_version_in_git(reference) do
|
|
165
|
+
shell_command.run!
|
|
166
|
+
code = "> #{shell_command.command}\n#{shell_command.stdout}"
|
|
167
|
+
end
|
|
168
|
+
[code,'shell']
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def read_file_in_git(path_in_repo,reference)
|
|
172
|
+
puts "Getting file at #{reference}"
|
|
173
|
+
code = nil
|
|
174
|
+
filename = path_in_repo
|
|
175
|
+
at_version_in_git(reference) do
|
|
176
|
+
code = File.read(path_in_repo)
|
|
177
|
+
end
|
|
178
|
+
[code, identify_language(path_in_repo),filename]
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def run_shell_command(shell_command)
|
|
182
|
+
shell_command.run!
|
|
183
|
+
["> #{shell_command.command}\n#{shell_command.stdout}",'shell']
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def css_class(language)
|
|
187
|
+
if language.nil? || language.strip == ''
|
|
188
|
+
""
|
|
189
|
+
else
|
|
190
|
+
" class=\"language-#{language}\""
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def filename_footer(filename)
|
|
195
|
+
if filename && filename.strip != ''
|
|
196
|
+
%{<footer><h1>#{filename}</h1></footer>}
|
|
197
|
+
else
|
|
198
|
+
''
|
|
199
|
+
end
|
|
66
200
|
end
|
|
67
201
|
end
|
|
68
202
|
end
|