all repos — markup @ 84523f565c8c3899362b2742896db99e3fd7fac4

The code we use to render README.your_favorite_markup

Merge pull request #231 from github/lazy-load-markups

Lazy load markup gems
Charlie Somerville charlie@charliesomerville.com
Tue, 03 Dec 2013 17:07:26 -0800
commit

84523f565c8c3899362b2742896db99e3fd7fac4

parent

41e43b8153b35f618a50c47f2e7e6c5467e44b77

M lib/github/markup.rblib/github/markup.rb

@@ -1,59 +1,41 @@

-begin - require 'open3_detach' -rescue LoadError - require 'open3' -end +require "github/markup/command_implementation" +require "github/markup/gem_implementation" module GitHub module Markup extend self - @@markups = {} + @@markups = [] + + def markups + @@markups + end + + def preload! + markups.each do |markup| + markup.load + end + end def render(filename, content = nil) content ||= File.read(filename) - if proc = renderer(filename) - proc[content] + if impl = renderer(filename) + impl.render(content) else content end end - def markup(file, pattern, &block) - require file.to_s - add_markup(pattern, &block) - true - rescue LoadError - false + def markup(file, pattern, opts = {}, &block) + markups << GemImplementation.new(pattern, file, &block) end def command(command, regexp, &block) - command = command.to_s - if File.exists?(file = File.dirname(__FILE__) + "/commands/#{command}") command = file end - add_markup(regexp) do |content| - rendered = execute(command, content) - rendered = rendered.to_s.empty? ? content : rendered - - if block && block.arity == 2 - # If the block takes two arguments, pass new content and old - # content. - block.call(rendered, content) - elsif block - # One argument is just the new content. - block.call(rendered) - else - # No block? No problem! - rendered - end - end - end - - def add_markup(regexp, &block) - @@markups[regexp] = block + markups << CommandImplementation.new(regexp, command, &block) end def can_render?(filename)

@@ -61,38 +43,13 @@ !!renderer(filename)

end def renderer(filename) - @@markups.each do |key, value| - if Regexp.compile("\\.(#{key})$") =~ filename - return value - end - end - nil - end - - def renderer_name(filename) - @@markups.each do |key, value| - if Regexp.compile("\\.(#{key})$") =~ filename - return key - end - end - nil - end - - def execute(command, target) - out = '' - Open3.popen3(command) do |stdin, stdout, _| - stdin.puts target - stdin.close - out = stdout.read - end - out.gsub("\r", '') - rescue Errno::EPIPE - "" - rescue Errno::ENOENT - "" + markups.find { |impl| + impl.match?(filename) + } end # Define markups - instance_eval File.read(File.dirname(__FILE__) + '/markups.rb') + markups_rb = File.dirname(__FILE__) + '/markups.rb' + instance_eval File.read(markups_rb), markups_rb end end
A lib/github/markup/command_implementation.rb

@@ -0,0 +1,52 @@

+begin + require "open3_detach" +rescue LoadError + require "open3" +end + +require "github/markup/implementation" + +module GitHub + module Markup + class CommandImplementation < Implementation + attr_reader :command, :block + + def initialize(regexp, command, &block) + super regexp + @command = command.to_s + @block = block + end + + def render(content) + rendered = execute(command, content) + rendered = rendered.to_s.empty? ? content : rendered + call_block(rendered, content) + end + + private + def call_block(rendered, content) + if block && block.arity == 2 + block.call(rendered, content) + elsif block + block.call(rendered) + else + rendered + end + end + + def execute(command, target) + out = '' + Open3.popen3(command) do |stdin, stdout, _| + stdin.puts target + stdin.close + out = stdout.read + end + out.gsub("\r", '') + rescue Errno::EPIPE + "" + rescue Errno::ENOENT + "" + end + end + end +end
A lib/github/markup/gem_implementation.rb

@@ -0,0 +1,26 @@

+require "github/markup/implementation" + +module GitHub + module Markup + class GemImplementation < Implementation + attr_reader :gem_name, :renderer + + def initialize(regexp, gem_name, &renderer) + super regexp + @gem_name = gem_name.to_s + @renderer = renderer + end + + def load + return if @loaded + require gem_name + @loaded = true + end + + def render(content) + load + renderer.call(content) + end + end + end +end
A lib/github/markup/implementation.rb

@@ -0,0 +1,28 @@

+module GitHub + module Markup + class Implementation + attr_reader :regexp + + def initialize(regexp) + @regexp = regexp + end + + def load + # no-op by default + end + + def render(content) + raise NotImplementedError, "subclasses of GitHub::Markup::Implementation must define #render" + end + + def match?(filename) + file_ext_regexp =~ filename + end + + private + def file_ext_regexp + @file_ext_regexp ||= /\.(#{regexp})\z/ + end + end + end +end
A lib/github/markup/markdown.rb

@@ -0,0 +1,56 @@

+require "github/markup/implementation" + +module GitHub + module Markup + class Markdown < Implementation + MARKDOWN_GEMS = { + "github/markdown" => proc { |content| + GitHub::Markdown.render(content) + }, + "redcarpet" => proc { |content| + RedcarpetCompat.new(content).to_html + }, + "rdiscount" => proc { |content| + RDiscount.new(content).to_html + }, + "maruku" => proc { |content| + Maruku.new(content).to_html + }, + "kramdown" => proc { |content| + Kramdown::Document.new(content).to_html + }, + "bluecloth" => proc { |content| + BlueCloth.new(content).to_html + }, + } + + def initialize + super(/md|mkdn?|mdwn|mdown|markdown|litcoffee/) + end + + def load + return if @renderer + MARKDOWN_GEMS.each do |gem_name, renderer| + if try_require(gem_name) + @renderer = renderer + return + end + end + raise LoadError, "no suitable markdown gem found" + end + + def render(content) + load + @renderer.call(content) + end + + private + def try_require(file) + require file + true + rescue LoadError + false + end + end + end +end
M lib/github/markups.rblib/github/markups.rb

@@ -1,24 +1,6 @@

-MD_FILES = /md|mkdn?|mdwn|mdown|markdown|litcoffee/ +require "github/markup/markdown" -if markup('github/markdown', MD_FILES) do |content| - GitHub::Markdown.render(content) - end -elsif markup(:redcarpet, MD_FILES) do |content| - RedcarpetCompat.new(content).to_html - end -elsif markup(:rdiscount, MD_FILES) do |content| - RDiscount.new(content).to_html - end -elsif markup(:maruku, MD_FILES) do |content| - Maruku.new(content).to_html - end -elsif markup(:kramdown, MD_FILES) do |content| - Kramdown::Document.new(content).to_html - end -elsif markup(:bluecloth, MD_FILES) do |content| - BlueCloth.new(content).to_html - end -end +markups << GitHub::Markup::Markdown.new markup(:redcloth, /textile/) do |content| RedCloth.new(content).to_html