docbrown/app/services/content_renderer.rb

68 lines
2.4 KiB
Ruby

# renders markdown for Articles, DisplayAds, Comments
class ContentRenderer
Result = Struct.new(:front_matter, :reading_time, :processed_html, keyword_init: true)
class_attribute :processor, default: MarkdownProcessor::Parser
class_attribute :front_matter_parser, default: FrontMatterParser::Parser.new(:md)
class ContentParsingError < StandardError
end
# @param input [String] body_markdown to process
# @param source [Article, Comment, DisplayAd]
# @param user [User, NilClass] article's or comment's user, nil for DisplayAd
# @param fixer [Object] fixes the input markdown
def initialize(input, source:, user: nil, fixer: MarkdownProcessor::Fixer::FixAll)
@input = input || ""
@source = source
@user = user
@fixer = fixer
end
# @param link_attributes [Hash] options passed further to RedCarpet::Render::HTMLRouge, example: { rel: "nofollow"}
# @param prefix_images_options [Hash] options for Html::Parser#prefix_all_images
# @return [ContentRenderer::Result]
def process(link_attributes: {},
prefix_images_options: { width: 800, synchronous_detail_detection: false })
fixed = fixer.call(input)
processed = processor.new(fixed, source: source, user: user)
processed_html = processed.finalize(link_attributes: link_attributes,
prefix_images_options: prefix_images_options)
Result.new(front_matter: nil, processed_html: processed_html, reading_time: 0)
rescue StandardError => e
raise ContentParsingError, e.message
end
# processes Article markdown, calculates reading time and finds frontmatter (if it exists)
#
# @return [ContentRenderer::Result]
def process_article
fixed = fixer.call(input)
parsed = front_matter_parser.call(fixed)
front_matter = parsed.front_matter
processed = processor.new(parsed.content, source: source, user: user)
reading_time = processed.calculate_reading_time
processed_html = processed.finalize
Result.new(front_matter: front_matter, processed_html: processed_html, reading_time: reading_time)
rescue StandardError => e
raise ContentParsingError, e.message
end
def has_front_matter?
fixed = fixer.call(input)
parsed = front_matter_parser.call(fixed)
front_matter = parsed.front_matter
front_matter.any? && front_matter["title"].present?
rescue ContentRenderer::ContentParsingError
true
end
private
attr_reader :fixer, :input, :user, :source
end