Markdown разметка в RubyOnRails-приложении

от автора


В одном новом проекте, написанном на ruby on rails, была поставлена задача дать возможность пользователю использовать markdown-разметку для форматирования текста. Один из вариантов реализации описан в данной статье.

Статья не описывает основы ruby on rails. Я подразумеваю, что читатель может создать проект и запустить его в своей любимой IDE / из консоли / еще как. Плюс в конце статьи есть ссылка на исходники демонстрационного проекта.
Одно уточнение: я использую haml для view-шек и twitter bootstrap для оформления.
Допустим что у нас есть приложение. И нам надо добавить в него класс «Статья» с полями заголовок и содержимое. Поле содержимое будет типа text. К нему то нам и необходимо применить markdown-разметку.

Добавим наш класс и заодно контроллер и view-шки

rails generate scaffold Post title content:text rake db:migrate 


Markdown парсер

В качестве парсера используем redcarpet

# Gemfile ... gem "redcarpet" 

Добавляем helper для вывода текста с использованием markdown разметки

# app/helpers/application_helper.rb module ApplicationHelper   def markdown(text)     renderer = Redcarpet::Render::HTML.new(hard_wrap: true, filter_html: true)     options = {         autolink: true,         no_intra_emphasis: true,         fenced_code_blocks: true,         lax_html_blocks: true,         strikethrough: true,         superscript: true,         space_after_headers: true     }     Redcarpet::Markdown.new(renderer, options).render(text).html_safe   end end 

Redcarpet::Markdown — сам markdown-парсер. Redcarpet::Render::HTML — один из рендереров. Конкретно этот генерирует html из markdown разметки. При желании можно написать свой рендерер или унаследовать от существующих. Возможные значения опций для рендерера и парсера можно посмотреть в документации.

Применяем helper для отображения содержимого

-# app/views/posts/show.html.haml %h3= @post.title %p= markdown(@post.content)  = link_to 'Edit', edit_post_path(@post) \| = link_to 'Back', posts_path 

Теперь если мы создадим статью со следующим содержимым

> this is quotes  List:  - item 1 - item 2 - item 3  [this is link](http://example.com)  ![image](http://placehold.it/350x150)  ```  class Cat < Animal    def say      "Meow!"    end  end  ``` 

то увидим примерно следующую картину


Markdown редактор

Добавим в проект визуальный редактор MarkItUp. Для этого можно использовать гем markitup-rails

# Gemfile ... gem 'markitup-rails' 

# app/assets/javascripts/application.js … //= require markitup …

// app/assets/stylesheets/application.css.scss … @import "markitup"; @import "markitup-markdown";

И наконец применяем редактор к нашему текстовому полю в форме редактирования статьи

# app/assets/javascripts/posts.js.coffee jQuery ->   markdownSettings = {     previewParserPath: '/markdown/preview'     onShiftEnter:	{keepDefault:false, openWith:'\n\n'}     markupSet: [       {         name:'First Level Heading', key:'1',         placeHolder:'Your title here...',         closeWith: (markItUp) -> markdownTitle(markItUp, '=')       },       {         name:'Second Level Heading', key:'2', placeHolder:'Your title here...',         closeWith: (markItUp) -> markdownTitle(markItUp, '-')       },       {name:'Heading 3', key:'3', openWith:'### ', placeHolder:'Your title here...' }       {name:'Heading 4', key:'4', openWith:'#### ', placeHolder:'Your title here...' }       {name:'Heading 5', key:'5', openWith:'##### ', placeHolder:'Your title here...' }       {name:'Heading 6', key:'6', openWith:'###### ', placeHolder:'Your title here...' }       {separator:'---------------' }       {name:'Bold', key:'B', openWith:'**', closeWith:'**'}       {name:'Italic', key:'I', openWith:'_', closeWith:'_'}       {separator:'---------------' }       {name:'Bulleted List', openWith:'- ' }       {name:'Numeric List', openWith: (markItUp) -> markItUp.line+'. ' }       {separator:'---------------' }       {         name:'Picture', key:'P',         replaceWith:'![[![Alternative text]!]]([![Url:!:http://]!] "[![Title]!]")'       },       {         name:'Link', key:'L', openWith:'[',         closeWith:']([![Url:!:http://]!] "[![Title]!]")',         placeHolder:'Your text to link here...'       },       {separator:'---------------'}       {name:'Quotes', openWith:'> '}       {name:'Code Block / Code', openWith:'(!(\t|!|`)!)', closeWith:'(!(`)!)'}       {separator:'---------------'}       {name:'Preview', call:'preview', className:"preview"}     ]   }    markdownTitle = (markItUp, char) ->     heading = '';     n = $.trim(markItUp.selection||markItUp.placeHolder).length;     for i in [0..n]       heading += char     '\n'+heading    $('#post_content').markItUp(markdownSettings) 

где post_content — id текстового поля. Настройки редактора (markdownSettings) взяты из документации.
И теперь форма редактирования выглядит примерно так


Предварительный просмотр

Стоит обратить внимание на одну строку

# app/assets/javascripts/posts.js.coffee ... previewParserPath: '/markdown/preview' 

Это путь к серверной странице которая сформирует превью. Реализуем ее

# app/controllers/markdown_controller.rb class MarkdownController < ApplicationController   def preview     @text = params[:data]   end end 

-# app/views/markdown/preview.html.haml = markdown(@text)

# config/routes.rb ... post "markdown/preview" 

И теперь у нас есть работающее превью (с указанными выше настройками вызывается последней кнопкой на панели редактора)

Полезные ссылки:

ссылка на оригинал статьи http://habrahabr.ru/company/centosadmin/blog/163947/