Мартин обнови решението на 23.11.2011 22:49 (преди около 13 години)
+# encoding: utf-8
+class Formatter
+ attr_reader :plain
+
+ def initialize(plain)
+ @plain = plain
+ @html = @plain.dup.rstrip()
+ end
+
+ def tags_compose
+ Header.compose(@html)
+ OrderedList.compose(@html)
+ UnOrderedList.compose(@html)
+ BlockQuote.compose(@html)
+ HyperLink.compose(@html)
+ end
+
+ def to_html
+ WhiteSpaceCleaner.clean(@html)
+ SpecialSymbols.compose(@html)
+ CodeBlock.compose(@html)
+ TagWrapper.wrap(EmAndStrong, @html, ['pre'])
+ tags_compose
+ TagWrapper.wrap(Paragraph, @html, ['h','pre','blockquote','ul','ol'])
+ TagCleaner.clean(@html)
+ paragraphs_in_blockquote
+ end
+
+ def paragraphs_in_blockquote
+ tag_order = []
+ @html.gsub!(/(?<=<blockquote>)(.+?)(?=<\/blockquote>$)/m) { |m| Paragraph.compose(m) }
+ TagCleaner.clean(@html)
+ @html
+ end
+
+ alias :to_s :to_html
+
+ def inspect
+ @plain
+ end
+end
+
+ class CodeBlock
+ def self.compose(text)
+ text.gsub!(/^[ ]{4,}.*/) { |m| m.gsub!($&, "<pre><code>#{$&[4..-1]}</code></pre>") }
+ end
+ end
+
+ class BlockQuote
+ def self.compose(text)
+ text.gsub!(/^>[ ]+(.*)/) do |m|
+ m.gsub!($&, "<blockquote>#{$1.strip()}</blockquote>")
+ end
+ end
+ end
+
+ class Header
+ def self.compose(text)
+ text.gsub!(/^([#]{1,4})\s+(.+)/) do |m|
+ m.gsub!($&, "<h#{$1.size}>#{$2.strip()}</h#{$1.size}>")
+ end
+ end
+ end
+
+ class HyperLink
+ def self.compose(text)
+ text.gsub!(/\[(.+)\]\((.+)\)/) do |m|
+ m.gsub!($&, "<a href=\"#{$2}\">#{$1}</a>")
+ end
+ end
+ end
+
+ class OrderedList
+ def self.compose(text)
+ text.gsub!(/^\d\.\s+(.+)/) do |m|
+ m.gsub!($&, "<ol>\n <li>#{$1.strip()}</li>\n</ol>")
+ end
+ end
+ end
+
+ class UnOrderedList
+ def self.compose(text)
+ text.gsub!(/^\*\s+(.+)/) do |m|
+ m.gsub!($&, "<ul>\n <li>#{$1.strip()}</li>\n</ul>")
+ end
+ end
+ end
+
+ class Strong
+ def self.regex
+ /.*?(\*{2}(.+?)\*{2}).*/
+ end
+
+ def self.compose(text)
+ text.gsub!(self.regex) { |m| m.gsub!($1, "<strong>#{$2.strip()}</strong>") }
+ end
+ end
+
+ class Em
+ def self.regex
+ /_(.+?)_/
+ end
+
+ def self.compose(text)
+ text.gsub!(self.regex) { |m| m.gsub!($&, "<em>#{$1.strip()}</em>") }
+ end
+ end
+
+ class EmAndStrong
+ def self.compose(text)
+ Strong.compose(text)
+ Em.compose(text)
+ end
+ end
+
+ class SpecialSymbols
+ def self.compose(text)
+ text.gsub!("&","&")
+ text.gsub!("<", "<")
+ text.gsub!(/(.+)(>)/) { |m| m.gsub!($2, ">") }
+ text.gsub!('"',""")
+ end
+ end
+
+ class TagCleaner
+ def self.clean(text)
+ 2.times do # First for </pre><pre>, then for </code><code>
+ text.scan(/<\/([^<]+?)>$/).each do |m|
+ text.gsub!("</#{m.first}>\n<#{m.first}>","\n")
+ end
+ end
+ text.gsub!("</li>\n\n","</li>")
+ end
+ end
+
+ class Paragraph
+ def self.compose(text)
+ text.gsub!(/^[^\$\n].+/) { |m| m.gsub!(m, "<p>#{m.strip()}</p>") }
+ end
+ end
+
+ class Collapser
+
+ def self.extract_tags(text, tags)
+ @@tags_order = []
+ tags.each_with_index do | tag, idx |
+ tag_seq = []
+ text.gsub!(/<#{tag}.+?<\/#{tag}\d?>$/m) do |m|
+ tag_seq << m.dup
+ m.replace("$#{idx}")
+ end
+ @@tags_order[idx] = tag_seq
+ end
+ end
+
+ def self.put_tags_back(text, tags)
+ raise RuntimeError, "You must perform extract_tags first" if @@tags_order.empty?
+ tags.each_with_index do | tag, idx |
+ next if @@tags_order[idx].nil?
+ @@tags_order[idx].each { |m| text.sub!(/\$#{idx}/, m) }
+ #if tag == 'blockquote'
+ # m.gsub!(/<\/?blockquote>/,"")
+ # Paragraph.compose(m)
+ # m.replace("<blockquote>#{m}</blockquote>")
+ # end
+ end
+ end
+ end
+
+ class WhiteSpaceCleaner
+ def self.clean(text)
+ text.gsub!(/^[ \t]{,3}(?=[^ \t])/,"")
+ text.gsub!(/\A\n+/,"")
+ end
+ end
+
+ class TagWrapper
+ def self.wrap(class_type, text, tags)
+ Collapser.extract_tags(text, tags)
+ class_type.compose(text)
+ Collapser.put_tags_back(text, tags)
+ end
+ end
Трябваше да сложа всичко в един модул...Ама кой да се сети...