В одном новом проекте, написанном на 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"
И теперь у нас есть работающее превью (с указанными выше настройками вызывается последней кнопкой на панели редактора)
Полезные ссылки:
- Markdown
- Redcarpet
- MarkItUp
- Исходники проекта из данной статьи
- Наш новый проект, где можно посмотреть реальное применение описанного в статье алгоритма