diff --git a/lib/jekyll/converters/markdown.rb b/lib/jekyll/converters/markdown.rb index 485cac82..178eaaa8 100644 --- a/lib/jekyll/converters/markdown.rb +++ b/lib/jekyll/converters/markdown.rb @@ -8,20 +8,22 @@ module Jekyll def setup return if @setup - @parser = case @config['markdown'] - when 'redcarpet' - RedcarpetParser.new @config - when 'kramdown' - KramdownParser.new @config - when 'rdiscount' - RDiscountParser.new @config - when 'maruku' - MarukuParser.new @config + @parser = + case @config['markdown'].downcase + when 'redcarpet' then RedcarpetParser.new(@config) + when 'kramdown' then KramdownParser.new(@config) + when 'rdiscount' then RDiscountParser.new(@config) + when 'maruku' then MarukuParser.new(@config) else - STDERR.puts "Invalid Markdown processor: #{@config['markdown']}" - STDERR.puts " Valid options are [ maruku | rdiscount | kramdown | redcarpet ]" - raise FatalException.new("Invalid Markdown process: #{@config['markdown']}") - end + # So they can't try some tricky bullshit or go down the ancestor chain, I hope. + if allowed_custom_class?(@config['markdown']) + self.class.const_get(@config['markdown']).new(@config) + else + Jekyll.logger.error "Invalid Markdown Processor:", "#{@config['markdown']}" + Jekyll.logger.error "", "Valid options are [ maruku | rdiscount | kramdown | redcarpet ]" + raise FatalException, "Invalid Markdown Processor: #{@config['markdown']}" + end + end @setup = true end @@ -38,6 +40,19 @@ module Jekyll setup @parser.convert(content) end + + private + + # Private: Determine whether a class name is an allowed custom markdown + # class name + # + # parser_name - the name of the parser class + # + # Returns true if the parser name contains only alphanumeric characters + # and is defined within Jekyll::Converters::Markdown + def allowed_custom_class?(parser_name) + parser_name !~ /[^A-Za-z0-9]/ && self.class.constants.include?(parser_name.to_sym) + end end end end diff --git a/site/docs/extras.md b/site/docs/extras.md index 0c190ec1..14261e34 100644 --- a/site/docs/extras.md +++ b/site/docs/extras.md @@ -16,7 +16,12 @@ Maruku comes with optional support for LaTeX to PNG rendering via blahtex Maruku to not assume a fixed location for `dvips`, check out [Remi’s Maruku fork](http://github.com/remi/maruku). -## RDiscount +## Alternative Markdown Processors + +While Jekyll defaults to using Maruku for Markdown conversion, you may use one +of the other three pre-defined markdown parsers or define your own. + +### RDiscount If you prefer to use [RDiscount](http://github.com/rtomayko/rdiscount) instead of [Maruku](http://github.com/bhollis/maruku) for Markdown, just make sure you have @@ -34,7 +39,7 @@ have Jekyll run with that option. markdown: rdiscount {% endhighlight %} -## Kramdown +### Kramdown You can also use [Kramdown](http://kramdown.rubyforge.org/) instead of Maruku for Markdown. Make sure that Kramdown is installed: @@ -54,3 +59,34 @@ Kramdown has various options for customizing the HTML output. The [Configuration](/docs/configuration/) page lists the default options used by Jekyll. A complete list of options is also available on the [Kramdown website](http://kramdown.rubyforge.org/options.html). + +### User-Defined + +So, you're totally at odds with our four built-in markdown parsers, eh? No +sweat. You can define one as a plugin: + +{% highlight ruby %} +require 'jekyll' +require 'some_renderer' + +class Jekyll::Converters::Markdown::MyCustomParser + def initialize(config) + @site_config = config + end + + def convert(content) + # (this _must_ return the resulting String after the rendering) + SomeRenderer.new(@site_config).to_html(content) + end +end +{% endhighlight %} + +Once you've got that setup, ask Jekyll to use your custom markdown parser in +your `_config.yml` file: + +{% highlight yaml %} +markdown: MyCustomParser +{% endhighlight %} + +(Note that this **is case-sensitive**, and is only the piece after +`Jekyll::Converters::Markdown`.) And there you are! diff --git a/test/test_site.rb b/test/test_site.rb index a530f9d0..04e2b3d0 100644 --- a/test/test_site.rb +++ b/test/test_site.rb @@ -262,6 +262,52 @@ class TestSite < Test::Unit::TestCase end end + context 'using a non-default markdown processor in the configuration' do + should 'use the non-default markdown processor' do + class Jekyll::Converters::Markdown::CustomMarkdown + def initialize(*args) + @args = args + end + + def convert(*args) + "" + end + end + + custom_processor = "CustomMarkdown" + s = Site.new(Jekyll.configuration.merge({ 'markdown' => custom_processor })) + assert_nothing_raised do + s.process + end + + # Do some cleanup, we don't like straggling stuff's. + Jekyll::Converters::Markdown.send(:remove_const, :CustomMarkdown) + end + + should 'ignore, if there are any bad characters in the class name' do + module Jekyll::Converters::Markdown::Custom + class Markdown + def initialize(*args) + @args = args + end + + def convert(*args) + "" + end + end + end + + bad_processor = "Custom::Markdown" + s = Site.new(Jekyll.configuration.merge({ 'markdown' => bad_processor })) + assert_raise Jekyll::FatalException do + s.process + end + + # Do some cleanup, we don't like straggling stuff's. + Jekyll::Converters::Markdown.send(:remove_const, :Custom) + end + end + context 'with an invalid markdown processor in the configuration' do should 'not throw an error at initialization time' do bad_processor = 'not a processor name'