module MarkdownProcessor module Fixer # Services here in the Fixer module should inherit from this base class. # #call is implemented here so other services only need to define a # METHODS constant that is an Array of symbols referencing other methods # found here. # # For example # METHODS = %i[add_quotes_to_tile add_quotes_to_description] class Base FRONT_MATTER_DETECTOR = /-{3}.*?-{3}/m def self.call(markdown) return unless markdown fix_methods.reduce(markdown) { |acc, elem| public_send(elem, acc) } end def self.fix_methods self::METHODS end def self.add_quotes_to_title(markdown) add_quotes_to_section(markdown, section: "title") end def self.add_quotes_to_description(markdown) add_quotes_to_section(markdown, section: "description") end def self.lowercase_published(markdown) markdown.gsub(/-{3}.*?-{3}/m) do |front_matter| front_matter.gsub(/^published: /i, "published: ") end end def self.convert_new_lines(markdown) markdown.gsub("\r\n", "\n") end def self.split_tags(markdown) markdown.gsub(/\ntags:.*\n/) do |tags| tags.split(" #").join(",").delete("#").gsub(":,", ": ") end end def self.underscores_in_usernames(markdown) return markdown unless markdown.match?(USERNAME_WITH_UNDERSCORE_REGEXP) traverser = MarkdownProcessor::Traverser.new(markdown) traverser.each do |line| next if traverser.in_codeblock? escape_underscored_username_in_line!(line) end.join end # Match @_username_ that is not preceded by backtick USERNAME_WITH_UNDERSCORE_REGEXP = /(?.*?)(\r\n|\n)/m) do |target| # `content` is the captured group (.*?) captured_text = Regexp.last_match("content") # The query below checks if the whole text is wrapped in # either single or double quotes. match = captured_text.scan(/(^".*"$|^'.*'$)/) if match.empty? # Double quotes that aren't already escaped will get escaped. # Then the whole text get warped in double quotes. parsed_text = captured_text.gsub(/(?