万神劫

万物天地为剑,神鬼妖邪为剑
劫波万渡,宇宙苍穹尽为剑
是为万神劫!

3条评论 2012-06-05

Klog开发笔记——Markdown 与 Redcarpet 简介

什么是 Markdown

做博客程序肯定会需要富文本这么个玩意儿,否则博客文章就只能纯文本了
做富文本最出名的肯定是 CKEditor 之类的东西,不过那种东西调整格式太麻烦,而且总会有些乱七八糟的问题,我这种有洁癖的人极不喜欢
因此我果断选择了 Github 所采用的方案:**Markdown 格式**
Markdown 是一种轻量级的标记语言,它通过一些特殊标记来表达格式,并在最终渲染时通过 Markdown解析器 将 Markdown 文本转换为 HTML 格式
比如以下这段 Markdown 文本

# Header1
## Header 2
* List1
* List2

经过转换后就变成以下的 HTML

<h1>Header1</h1>
<h2>Header2</h2>
<ul>
  <li>List1</li>
  <li>List2</li>
</ul>

关于 Markdown 语法请参考这里 Markdown 语法说明,基本语法非常简单

Redcarpet简介

我在开发 Klog 时选择了 Redcarpet 这个使用者较多的 Markdown 解析器

基本使用

Redcarpet 使用非常简单,只需要以下两行即可完成 Markdown 格式到 HTML 格式的转换

markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML)
html = markdown.render("This is *bongos*, indeed.")

首先新建一个 Markdown 的实例,然后调用实例的 render() 方法即可

构造方法参数定制

在新建实例时,可以通过参数定制解析器的一些行为,比如

markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML, :autolink => true, :space_after_headers => true)

参数中比较重要的有以下几个

  • :no_intra_emphasis 不解析单词中的下划线,比如 foo_bar_baz 不会被解析成带 的文本

  • :fenced_code_blocks 解析代码块,使用3个或3个以上 ~ 或者 ` 包围起来的文本会被解析为代码块
    你可以在开头指定代码的语言类型,具体格式形如这样

  • autolink 自动检查文本中 http https ftp 等协议的链接文本,将之解析为链接
    没有以 http:// 开头,而是直接以 www. 开头的同样会被检查到

  • :strikethrough 支持两个 ~ 包围的文本,解析为删除线

  • :space_after_headers #后面必须加空格,否则不会被解析为标题

我的博客程序采用的参数组合是 :autolink => true, :fenced_code_blocks => true, :no_intra_emphasis => true

Render 类构造方法参数定制

注意创建实例时的第一个参数 Redcarpet::Render::HTML ,这个类会根据默认参数自动被实例化,当然你也可以通过参数来定制这个类的行为,比如

rndr = Redcarpet::Render::HTML.new(:no_links => true, :hard_wrap => true)
markdown = Redcarpet::Markdown.new(rndr,  :autolink => true, :space_after_headers => true)

关于 Render 类的参数,比较重要的有以下几个

  • :filter_html 过滤 HTML ,换而言之,用户输入的 HTML 都会被转义后输出,建议设置为 true 以避免安全问题
  • :no_styles 不生成任何 标签
  • :with_toc_data 给生成的 Header 标签增加锚点
  • :hard_wrap 如果 Markdown 文本中有折行的话,会转换为 标签
  • :xhtml 生成符合 xhtml 标准的 HTML

自定义 Render 类

除了通过参数定制 Render 之外,如果希望进行一些更高级功能的定制,可以通过自己编写 Render 类来实现,以下是一个简单的示例

class Render < Redcarpet::Render::HTML
    def header(text, header_level)
      return "<h3>#{text}</h3>"
    end
end

markdown = Redcarpet::Markdown.new(Render)

使用这个 markdown 对象去转换 ,会将 header 统一转换为 标签,而默认会根据层级生成 等等
这里我们是通过覆盖 header() 方法实现了这一功能,其他可以通过覆盖来实现渲染功能定制的方法还有很多,比如

与 Rails 结合

考虑到效率问题,我给博客文章这张表增加了一个 html_content 的字段,然后通过 callback ,在每次保存前将 Markdown 文本转换为 HTML 写入这个字段

class Blog < ActiveRecord::Base
  before_save :fill_html_content

  #将markup的content转换为html并写入字段
  def fill_html_content
    self.html_content = Klog::Markdown.render(self.content)
  end
end

另外为了避免每次都要新建 Markdown 对象,我使用了单例模式,代码参考如下

module Klog
  class Markdown
    def self.render(text)
      return self.instance.render(text)
    end

    def self.instance
      @markdown ||= Redcarpet::Markdown.new(Klog::Render.new,  :autolink => true, :fenced_code_blocks => true,no_intra_emphasis => true)
    end
  end

  class Render < Redcarpet::Render::HTML
    def initialize(extensions={})
      super(extensions.merge(:xhtml => true, :no_styles => true, :filter_html => true, :hard_wrap => true))
    end
  end
end
comments powered by Disqus