如何从Sinatra实现Github样式的Markdown渲染?

4

TL;DR - 我如何使用类似于 improved_markdown :some_file 的东西进行自定义渲染,但仍然按照通常的方式呈现布局?


通常,在Sinatra中渲染Markdown,您只需要执行以下操作:

markdown :some_file

但我想添加能够执行“围栏式”语法高亮显示的功能,就像在Github README文件中一样。

```ruby
class Foo
  # etc
end
```

我已经部分完成了这个。

首先,我安装了Redcarpet并添加了一个自定义渲染类,它使用Pygments.rb来进行语法高亮:

# create a custom renderer that allows highlighting of code blocks
class HTMLwithPygments < Redcarpet::Render::HTML
  def block_code(code, language)
    Pygments.highlight(code, lexer: language)
  end
end

然后我在路由中使用它,像这样:
# Try to load any Markdown file specified in the URL
get '/*' do
  viewname = params[:splat].first

  if File.exist?("views/#{viewname}.md")

    # Uses my custom rendering class
    # The :fenced_code_blocks option means it will take, for example,
    # the word 'ruby' from ```ruby and pass that as the language
    # argument to my block_code method above  
    markdown_renderer = Redcarpet::Markdown.new(HTMLwithPygments, :fenced_code_blocks => true)

    file_contents = File.read("views/#{viewname}.md")
    markdown_renderer.render(file_contents)

  else
    "Nopers, I can't find it."
  end
end

几乎 可以使用。Markdown 被渲染为 HTML,并附加了用于高亮的标记。

唯一的问题是它不使用我的布局;毕竟,我只是读取文件并返回渲染后的字符串。正常的 markdown :foo 调用会涉及 Tilt 的过程。

我必须创建一个自定义的 Tilt 模板引擎来使其工作,还是有更简单的方法?

3个回答

3
您可以向markdown方法(或任何其他呈现方法)传递任意选项,并将它们传递给相关的Tilt模板。 Tilt的Redcarpet模板在创建其渲染器时寻找任何提供的:renderer选项,从而使您能够指定自定义选项。
您还可以通过将它们作为第二个参数传递给set :markdown, :option => :value来指定应应用于所有markdown调用的选项。
不过,这并不是那么简单,因为当前(发布的)版本的Tilt不能正确检测您是否安装了Redcarpet 2。不过您可以明确告诉它。
# first ensure we're using the right Redcarpet version
Tilt.register Tilt::RedcarpetTemplate::Redcarpet2, 'markdown', 'mkd', 'md'

# set the appropriate options for markdown
set :markdown, :renderer => HTMLwithPygments,
  :fenced_code_blocks => true, :layout_engine => :haml

现在任何对markdown的调用都将使用您自定义的代码块,并将layout.haml作为布局。(免责声明:我无法让Pygments工作(它会导致Sinatra崩溃),但这里的其他所有内容都有效(我使用了一个简单的自定义block_code方法,只是添加了一条消息,以便我可以知道它是否有效)。

太好了!不确定你是否遇到了我在答案中描述的相同崩溃,但我发现如果我使用 rackup config.ru,它会发生,但如果我在Passenger上运行,则不会。 - Nathan Long
@NathanLong 我解决了崩溃的问题 - 一番搜索得出的建议是,这与后台线程中Python的初始化有关,并且在启动时调用Pygments.start应该可以解决它。果然,在添加了那一行代码后,Pygments工作正常了。 - matt

0

一种解决方法

我目前正在这样做:

if File.exist?("views/#{viewname}.md") 
  CustomMarkdown.render(File.read("views/#{viewname}.md"))

它使用:

module CustomMarkdown
  def self.render(markdown_string)
    content = renderer.render(markdown_string)
    layout.render { content }
  end
  def self.renderer
    @markdown_renderer ||= Redcarpet::Markdown.new(HTMLwithPygments, :fenced_code_blocks => true)
  end
  def self.layout
    # Yes, this is hardcoded; in my simple app, I always use this layout.
    Tilt['haml'].new do
      File.read("views/layout.haml")
    end
  end
end

这个代码运行得还不错。我注意到一个没有关键字的围栏块,就像这样:

```
  code of unspecified type
```
# vs
```ruby
  explicitly ruby code
```

...导致我的Sinatra应用程序崩溃。我认为这意味着Python层中存在错误,因为我无法捕获任何抛出的错误。

更新: 当我使用Passenger时,不会发生崩溃,但是在rackup config.ru时会发生。


0
你可以使用 zzak 的 Glorify gem 来完成这种工作。

如果你只想要语法高亮的代码块而不需要其他功能,那么你可以使用它。但是尽管它所说的是Markdown渲染器,实际上它是一个RDoc渲染器,这两者并不相同(例如,它对于除了Ruby文档之外的任何内容都没有用处)。 - user2261892
rdoc现在也支持Markdown,但我还没有检查gem是否也支持。 - three
我最近发现了这个讨论,不得不说我对Glorify感到非常失望。在设置Github风格的Markdown时遇到了麻烦。不确定还有谁在使用RDoc,但如果那是你需要的话,那就使用Glorify吧。 - Dan Bradbury

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接