При написании статей уже давно не принято использовать простую HTML-разметку. Набирать каждый раз HTML-теги вручную — не самое приятное времяпрепровождение. Зачастую для этих целей используются user-friendly редакторы а-ля TinyMCE. И выглядят неплохо, и справиться с ними в силах любой необременённый излишками знания пользователь. Минус один: html-разметка, получаемая на выходе, может совсем не радовать глаз автора и мохнатые лапки поисковых «пауков».
По другую сторону сто́ят облегчённые языки разметки, самым популярным из которых является markdown. Вот его-то мы и будем подключать к Rails-проекту.
Для начала установим необходимые гемы. Для этого их следует прописать в Gemfile:
# Gemfile
gem 'redcarpet', '~> 3.0.0'
gem 'pygments.rb'
И дать команду:
$ bundle install
После чего обратимся к созданию helper'а, призванного сгенерировать html из
файла с разметкой markdown. За генерацию страниц в html из markdown в
ответе redcarpet
. К тому же благодаря pygments.rb
у нас появляется
возможность подсветки синтаксиса в коде.
# helpers/application_helper.rb
module ApplicationHelper
class HTMLwithPygments < Redcarpet::Render::HTML
require 'pygments.rb'
def block_code(code, language)
Pygments.highlight(code, :lexer => language)
end
end
def markdown(text, options = {})
renderer = HTMLwithPygments.new(hard_wrap: true)
options={
autolink: true,
no_intra_emphasis: true,
fenced_code_blocks: true,
lax_html_blocks: true,
strikethrough: true,
superscript: true,
space_after_headers: true,
underline: true,
highlight: true,
quote: true
}
Redcarpet::Markdown.new(renderer, options).render(text).html_safe
end
end
Для подсветки синтаксиса нужно написать блок кода и указать необходимый ЯП, вот так:
~~~ruby
puts "Hello!"
~~~
Функция markdown
принимает на вход текст с указанными опциями и генерирует html-страницу. Подробно об имеющихся опциях и их значении можно прочесть на странице проекта.
Осталось лишь вывести наш текст на обозрение массам:
<%# posts/show.html.erb %>
<%= link_to @post.title, post_path(@post) %>
<%= markdown @post.content %>
С этим способом в базе данных текст хранится в формате markdown, а перед тем,
как страница будет отображена, redcarpet
переводит её в html-формат.
С точки зрения производительности не самый лучший вариант.
Но есть и другой способ: сгенерировать html перед тем, как сохранять файл
в базу данных при помощи before_save
.
# post.rb
#
# Table name: posts
#
# id :integer not null, primary key
# title :string(255)
# content :text
# slug :string(255)
# summary :string(255)
# created_at :datetime
# updated_at :datetime
class Post < ActiveRecord::Base
before_save :render_content
def render_content
require 'redcarpet'
renderer = HTMLwithPygments
extensions = {fenced_code_blocks: true}
redcarpet = Redcarpet::Markdown.new(renderer, extensions)
self.content = redcarpet.render self.content
end
class HTMLwithPygments < Redcarpet::Render::HTML
require 'pygments.rb'
def block_code(code, language)
Pygments.highlight(code, :lexer => language)
end
end
end
Если ресурс не относится к техническим и статья не предназначена для
вывода блоков кода можно обойтись без pygments
:
# post.rb
class Post < ActiveRecord::Base
before_save :render_content
def render_content
require 'redcarpet'
renderer = Redcarpet::Render::HTML.new
extensions = {fenced_code_blocks: true}
redcarpet = Redcarpet::Markdown.new(renderer, extensions)
self.content = redcarpet.render self.content
end
end
Теперь в базе будет лежать готовая html-страница.
Для корректного вывода такой странички нам нужно использовать
стандартный фильтр html_safe
:
<%# posts/show.html.erb %>
<%= link_to @post.title, post_path(@post) %>
<%= @post.content.html_safe %>
Ещё раз: в первом примере (при генерации «на лету») страница
сохраняется в формате markdown, каждый раз перед тем как вывести её
содержимое redcarpet
генерирует текст из markdown в html.
Во втором примере отображается предварительно сгенерированный html-код. Так что вы вольны выбирать, какой из вариантов удобнее в каждом конкретном случае.