Pull to refresh
69.42
Слёрм
Учебный центр для тех, кто работает в IT

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

Reading time 4 min
Views 9.7K

В одном новом проекте, написанном на 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"

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

Полезные ссылки:
Tags:
Hubs:
+3
Comments 4
Comments Comments 4

Articles

Information

Website
slurm.io
Registered
Founded
Employees
51–100 employees
Location
Россия
Representative
Антон Скобин