lib/bookingit/renderer.rb in bookingit-0.0.1 vs lib/bookingit/renderer.rb in bookingit-0.1.0

- old
+ new

@@ -4,65 +4,199 @@ module Bookingit class Renderer < Redcarpet::Render::HTML include FileUtils - attr_accessor :headers + def initialize(config) + super() + options = config.rendering_config + additional_languages = Hash[(options[:languages] || {}).map { |ext_or_regexp,language| + if ext_or_regexp.kind_of? String + [/#{ext_or_regexp}$/,language] + else + [ext_or_regexp,language] + end + }] + @language_identifiers = EXTENSION_TO_LANGUAGE.merge(additional_languages) + @basedir = String(options[:basedir]).strip + @basedir = '.' if @basedir == '' + @stylesheets = Array(options[:stylesheets]) + @theme = options[:theme] || "default" + @cachedir = options[:cache] + @config = config + @images = [] + end + + attr_accessor :headers, :stylesheets, :theme, :images + + def current_chapter=(chapter) + @chapter = chapter + end + def header(text,header_level,anchor) @headers[header_level] ||= [] @headers[header_level] << text - "<h#{header_level}>#{text}</h#{header_level}>" + if header_level == 2 + @chapter.add_section(text,anchor) + end + "<a name='#{anchor}'></a><h#{header_level+1}>#{text}</h#{header_level+1}>" end + def image(link, title, alt_text) + title = title.gsub(/'/,'"') if title + @images << link + "<img src='#{link}' alt='#{alt_text}' title='#{title}'>" + end + def doc_header @headers = {} - "" + Views::HeaderView.new(@stylesheets,@theme,@config).render end + def doc_footer + Views::FooterView.new(@chapter,@config).render + end + EXTENSION_TO_LANGUAGE = { - '.rb' => 'ruby', - '.html' => 'html', - '.scala' => 'scala', + /\.rb$/ => 'ruby', + /\.html$/ => 'html', + /\.scala$/ => 'scala', + /Gemfile$/ => 'ruby', } + + def identify_language(path) + @language_identifiers.select { |matcher,language| + path =~ matcher + }.values.first + end + + def block_code(code, language) - if code.strip =~ /file:\/\/(.*)$/ - path = $1 - code = File.read(path) - language = EXTENSION_TO_LANGUAGE.fetch(File.extname(path)) - elsif code.strip =~ /git:\/\/(.*)$/ - path = $1 - if path =~ /(^.*).git\/(.*)#([^#]+)$/ - repo_path = $1 - path_in_repo = $2 - reference = $3 - chdir repo_path do - if reference =~ /^(.+)\.\.(.+)$/ - code = `git diff #{reference}` - language = 'diff' - else - `git checkout #{reference} 2>&1` - code = File.read(path_in_repo) - `git checkout master 2>&1` - language = EXTENSION_TO_LANGUAGE.fetch(File.extname(path_in_repo)) + result = nil + filename = nil + chdir @basedir do + code,language,filename = CodeBlockInterpreter.new(code) + .when_file( &cache(:read_file)) + .when_git_diff( &cache(:read_git_diff)) + .when_shell_command_in_git(&cache(:run_shell_command_in_git)) + .when_file_in_git( &cache(:read_file_in_git)) + .when_shell_command( &cache(:run_shell_command)) + .otherwise { + [code,language,nil] + }.result + end + Views::CodeView.new(code,filename,language,@config).render.strip + end + + private + + def cache(method_name) + ->(*args) { + if @cachedir && File.exist?(cached_filename(*args)) + puts "Pulling from cache..." + lines = File.read(cached_filename(*args)).split(/\n/) + language = lines.shift + filename = lines.shift + [lines.join("\n") + "\n",language,filename] + else + code,language,filename = method(method_name).(*args) + if @cachedir + FileUtils.mkdir_p(@cachedir) unless File.exist?(@cachedir) + File.open(cached_filename(*args),'w') do |file| + file.puts language + file.puts filename + file.puts code end + puts "Cached output" end + [code,language,filename] + end + } + end + + def cached_filename(*args) + args = args.map { |arg| + case arg + when ShellCommand + [arg.command,arg.expected_exit_status].join("_") else - raise "You must provide a SHA1 or tagname: #{path}" + arg end - elsif code.strip =~ /sh:\/\/(.+)#([^#]+)$/ - path = $1 - command = $2 - chdir path do - output = `#{command}` - code = "> #{command}\n#{output}" - language = 'shell' + } + File.join(@cachedir,args.join('__').gsub(/[#\/\!\s><]/,'_')) + end + + def at_version_in_git(reference,&block) + ShellCommand.new(command: "git checkout #{reference} 2>&1").run! + block.call + ShellCommand.new(command: "git checkout master 2>&1").run! + end + + def capture_command_output(path,command,exit_type=:zero) + shell_command = ShellCommand.new(command: command,path: path) do |exit_status| + case exit_type + when :zero + exit_status == 0 + when :nonzero + exit_status != 0 + else + raise "unknown exit type #{exit_type}" end end - css_class = if language.nil? || language.strip == '' - "" - else - " class=\"language-#{language}\"" - end - %{<pre><code#{css_class}>#{CGI.escapeHTML(code)}</code></pre>} + shell_command.run! + ["> #{command}\n#{shell_command.stdout}",'shell'] + end + + def read_file(path) + filename = path + [File.read(path),identify_language(path),filename] + end + + def read_git_diff(path_in_repo,reference) + puts "Calculating git diff #{reference}" + filename = path_in_repo + shell_command = ShellCommand.new(command: "git diff #{reference} #{path_in_repo}") + shell_command.run! + [ shell_command.stdout, 'diff', filename ] + end + + def run_shell_command_in_git(reference,shell_command) + code = nil + at_version_in_git(reference) do + shell_command.run! + code = "> #{shell_command.command}\n#{shell_command.stdout}" + end + [code,'shell'] + end + + def read_file_in_git(path_in_repo,reference) + puts "Getting file at #{reference}" + code = nil + filename = path_in_repo + at_version_in_git(reference) do + code = File.read(path_in_repo) + end + [code, identify_language(path_in_repo),filename] + end + + def run_shell_command(shell_command) + shell_command.run! + ["> #{shell_command.command}\n#{shell_command.stdout}",'shell'] + end + + def css_class(language) + if language.nil? || language.strip == '' + "" + else + " class=\"language-#{language}\"" + end + end + + def filename_footer(filename) + if filename && filename.strip != '' + %{<footer><h1>#{filename}</h1></footer>} + else + '' + end end end end