Решение на Пета задача от Георги Христозов

Обратно към всички решения

Към профила на Георги Христозов

Резултати

  • 5 точки от тестове
  • 0 бонус точки
  • 5 точки общо
  • 38 успешни тест(а)
  • 19 неуспешни тест(а)

Код

module Element
class BaseElement
attr_reader :text
def initialize(text)
@text = [text]
end
def add_line(line)
@text = @text.concat(Array.new(line))
end
def strip
@text.map!(&:strip)
end
def inlineformatting
@text.map! do |line|
line.gsub(/\[{1}(?<text>.+)\]{1}\({1}(?<href>.+)\){1}/,
'<a href="\k<href>">\k<text></a>').
gsub(/\_{1}(?<text>.+?)\_{1}/, '<em>\k<text></em>').
gsub(/\*{2}(?<text>.+?)\*{2}/, '<strong>\k<text></strong>')
end
end
alias to_html to_s
def to_html
strip
inlineformatting
@text.join('\n')
end
end
class Heading1 < BaseElement
def to_html
"<h1>#{super.strip}</h1>"
end
end
class Heading2 < BaseElement
def to_html
"<h2>#{super.strip}</h2>"
end
end
class Heading3 < BaseElement
def to_html
"<h3>#{super.strip}</h3>"
end
end
class Heading4 < BaseElement
def to_html
"<h4>#{super.strip}</h4>"
end
end
class Code < BaseElement
def to_html
"<pre><code>#{@text.join}</code></pre>"
end
end
class Paragraph < BaseElement
def to_html
"<p>#{super}</p>"
end
end
class BlockQuote < BaseElement
def to_html
"<blockquote><p>#{super}</p></blockquote>"
end
end
class OrderedList < BaseElement
def to_html
separator = "</li>\n <li>"
"<ol>\n <li>#{super.split('\n').join(separator)}</li>\n</ol>"
end
end
class UnorderedList < BaseElement
def to_html
separator = "</li>\n <li>"
"<ul>\n <li>#{super.split('\n').join(separator)}</li>\n</ul>"
end
end
end
class Parser
attr_reader :lines
def initialize(text)
@lines = text.split("\n")
end
@@line_patterns =
{ Element::Heading1 => /^\#{1}\s+(?<text>\S+.+?)$/,
Element::Heading2 => /^\#{2}\s+(?<text>\S+.+?)$/,
Element::Heading3 => /^\#{3}\s+(?<text>\S+.+?)$/,
Element::Heading4 => /^\#{4}\s+(?<text>\S+.+?)$/,
Element::Code => /^\s{4}(?<text>.+)$/, Element::BlockQuote => /^>\s+(?<text>.+?)$/,
Element::UnorderedList => /^\*{1}\s{1}(?<text>.+)$/
}
@@ordered_list_pattern = /^(?<number>\d+)\.\s(?<text>.+)$/
def simple_parsing(line)
return line unless line.is_a? String
@@line_patterns.each do |k,v|
match = line.match(v)
return k.new(match[:text]) if match and match[:text]
end
line
end
def ordered_list(line, index)
return unless line.is_a? String
match = line.match @@ordered_list_pattern
return unless (match != nil and match[:number].to_i == 1)
@lines[index] = Element::OrderedList.new(match[:text])
ordere_list_inner(index)
end
def ordered_list_inner(parent_idx)
lines_to_delete = []
(parent_idx+1..@lines.length).each do |i|
next unless @lines[i].is_a? String
succ_match = @lines[i].match @@ordered_list_pattern
break unless succ_match and succ_match[:number].to_i == 1+i-parent_idx
@lines[parent_idx].add_line(succ_match[:text])
lines_to_delete << i
end
delete_line lines_to_delete
end
def delete_line lines
Array.new(lines).each do |i|
@lines.delete_at i
end
end
def make_paragraphs(line)
return line unless line.is_a? String
Element::Paragraph.new(line)
end
def join_multiline(lines)
(0...lines.length).each do |i|
next unless [Element::UnorderedList, Element::Code, Element::BlockQuote].
index(lines[i].class)
join_multiline_inner(lines, i)
end
lines
end
def join_multiline_inner(lines, parent_idx)
(parent_idx+1...lines.length).each do |i|
return unless (lines[i].is_a? lines[parent_idx].class)
lines[parent_idx].add_line lines[i].text
lines.delete_at i
end
end
def parse
@lines.map!(&method(:simple_parsing))
@lines.each_with_index(&method(:ordered_list))
@lines.reject! {|line| line.is_a? String and line.match(/^$/)}
@lines.map!(&method(:make_paragraphs))
@lines = join_multiline @lines
@lines
end
end
class SpecialCharacters
def self.substitute text
text.gsub(/\&/, "&amp;").
gsub(/(?<before>.+)\>/, '\k<before>&gt;').
gsub(/\</, "&lt;").
gsub(/\"/, "&quot;")
end
end
class OutputGenerator
def self.generate_markdown(lines)
lines.map(&:to_html).join("\n").gsub(/<\/p>\n<p>/,"</p>\n\n<p>").strip
end
end
class Formatter
def initialize(text)
@text = text
end
def to_s
to_html
end
def to_html
parser = Parser.new(SpecialCharacters.substitute(@text))
OutputGenerator.generate_markdown(parser.parse)
end
def inspect
@text
end
end

Лог от изпълнението

..F....F...FF.FF.FFF...F....FFFF...........F......FFF...F

Failures:

  1) Formatter paragraphs renders multiline paragraps
     Failure/Error: Formatter.new(plain).to_html.should eq formatted.strip
       
       expected: "<p>First line.\nSecond line.\nThird line.</p>\n\n<p>Last line, of course.</p>"
            got: "<p>First line.</p>\n\n<p>Second line.</p>\n\n<p>Third line.</p>\n\n<p>Last line, of course.</p>"
       
       (compared using ==)
       
       Diff:
       @@ -1,6 +1,8 @@
       -<p>First line.
       -Second line.
       -Third line.</p>
       +<p>First line.</p>
       +
       +<p>Second line.</p>
       +
       +<p>Third line.</p>
        
        <p>Last line, of course.</p>
     # /tmp/d20111129-16859-197z0bq/spec.rb:660:in `expect_transformation'
     # /tmp/d20111129-16859-197z0bq/spec.rb:45:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  2) Formatter headers renders tricky ones
     Failure/Error: Formatter.new(plain).to_html.should eq formatted.strip
       
       expected: "<h2>Leading wsp</h2>"
            got: "<p>## Leading wsp</p>"
       
       (compared using ==)
     # /tmp/d20111129-16859-197z0bq/spec.rb:660:in `expect_transformation'
     # /tmp/d20111129-16859-197z0bq/spec.rb:665:in `block in expect_transformations'
     # /tmp/d20111129-16859-197z0bq/spec.rb:664:in `each'
     # /tmp/d20111129-16859-197z0bq/spec.rb:664:in `expect_transformations'
     # /tmp/d20111129-16859-197z0bq/spec.rb:106:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  3) Formatter code blocks renders multiline blocks with empty lines
     Failure/Error: Formatter.new(plain).to_html.should eq formatted.strip
       
       expected: "<pre><code>require 'gravity'\n\n# I'm flying! Just like in Python!</code></pre>"
            got: "<pre><code>require 'gravity'</code></pre>\n<p></p>\n<pre><code># I'm flying! Just like in Python!</code></pre>"
       
       (compared using ==)
       
       Diff:
       @@ -1,4 +1,4 @@
       -<pre><code>require 'gravity'
       -
       -# I'm flying! Just like in Python!</code></pre>
       +<pre><code>require 'gravity'</code></pre>
       +<p></p>
       +<pre><code># I'm flying! Just like in Python!</code></pre>
     # /tmp/d20111129-16859-197z0bq/spec.rb:660:in `expect_transformation'
     # /tmp/d20111129-16859-197z0bq/spec.rb:140:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  4) Formatter code blocks renders multiple ones
     Failure/Error: Formatter.new(plain).to_html.should eq formatted.strip
       
       expected: "<pre><code>First code-block</code></pre>\n\n<pre><code>Second block of code</code></pre>"
            got: "<pre><code>First code-blockSecond block of code</code></pre>"
       
       (compared using ==)
       
       Diff:
       @@ -1,4 +1,2 @@
       -<pre><code>First code-block</code></pre>
       -
       -<pre><code>Second block of code</code></pre>
       +<pre><code>First code-blockSecond block of code</code></pre>
     # /tmp/d20111129-16859-197z0bq/spec.rb:660:in `expect_transformation'
     # /tmp/d20111129-16859-197z0bq/spec.rb:156:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  5) Formatter code blocks renders properly a longer example with tabs and Unicode
     Failure/Error: Formatter.new(plain).to_html.should eq formatted.strip
       
       expected: "<pre><code>// \u041F\u0440\u0438\u043C\u0435\u0440 \u0437\u0430 \u0431\u043B\u043E\u043A \u0441 \u043A\u043E\u0434.\n// \u0412 \u043D\u0435\u0433\u043E \u0432\u0441\u0435\u043A\u0438 \u0440\u0435\u0434, \u0434\u043E\u0440\u0438 \u043F\u0440\u0430\u0437\u043D\u0438\u0442\u0435, \u0435 \u043F\u0440\u0435\u0434\u0448\u0435\u0441\u0442\u0432\u0430\u043D \u043E\u0442 \u0442\u043E\u0447\u043D\u043E \u0447\u0435\u0442\u0438\u0440\u0438 \u0438\u043D\u0442\u0435\u0440\u0432\u0430\u043B\u0430.\ninclude &lt;stdio.h&gt;\n\nint main(int, char**) {\n\t// Whitespace \u0441\u043B\u0435\u0434 \u0447\u0435\u0442\u0438\u0440\u0438\u0442\u0435 \u0437\u0430\u0434\u044A\u043B\u0436\u0438\u0442\u0435\u043B\u043D\u0438 \u0438\u043D\u0442\u0435\u0440\u0432\u0430\u043B\u0430 \u0432 \u043D\u0430\u0447\u0430\u043B\u043E\u0442\u043E, \u0441\u0435 \u0437\u0430\u043F\u0430\u0437\u0432\u0430 \u0432\u0438\u043D\u0430\u0433\u0438.\n\treturn 42;\n}</code></pre>"
            got: "<pre><code>// \u041F\u0440\u0438\u043C\u0435\u0440 \u0437\u0430 \u0431\u043B\u043E\u043A \u0441 \u043A\u043E\u0434.// \u0412 \u043D\u0435\u0433\u043E \u0432\u0441\u0435\u043A\u0438 \u0440\u0435\u0434, \u0434\u043E\u0440\u0438 \u043F\u0440\u0430\u0437\u043D\u0438\u0442\u0435, \u0435 \u043F\u0440\u0435\u0434\u0448\u0435\u0441\u0442\u0432\u0430\u043D \u043E\u0442 \u0442\u043E\u0447\u043D\u043E \u0447\u0435\u0442\u0438\u0440\u0438 \u0438\u043D\u0442\u0435\u0440\u0432\u0430\u043B\u0430.</code></pre>\n<pre><code>include &lt;stdio.h&gt;</code></pre>\n<p></p>\n<pre><code>int main(int, char**) {\t// Whitespace \u0441\u043B\u0435\u0434 \u0447\u0435\u0442\u0438\u0440\u0438\u0442\u0435 \u0437\u0430\u0434\u044A\u043B\u0436\u0438\u0442\u0435\u043B\u043D\u0438 \u0438\u043D\u0442\u0435\u0440\u0432\u0430\u043B\u0430 \u0432 \u043D\u0430\u0447\u0430\u043B\u043E\u0442\u043E, \u0441\u0435 \u0437\u0430\u043F\u0430\u0437\u0432\u0430 \u0432\u0438\u043D\u0430\u0433\u0438.}</code></pre>\n<pre><code>\treturn 42;</code></pre>"
       
       (compared using ==)
       
       Diff:
       @@ -1,9 +1,6 @@
       -<pre><code>// Пример за блок с код.
       -// В него всеки ред, дори празните, е предшестван от точно четири интервала.
       -include &lt;stdio.h&gt;
       -
       -int main(int, char**) {
       -	// Whitespace след четирите задължителни интервала в началото, се запазва винаги.
       -	return 42;
       -}</code></pre>
       +<pre><code>// Пример за блок с код.// В него всеки ред, дори празните, е предшестван от точно четири интервала.</code></pre>
       +<pre><code>include &lt;stdio.h&gt;</code></pre>
       +<p></p>
       +<pre><code>int main(int, char**) {	// Whitespace след четирите задължителни интервала в началото, се запазва винаги.}</code></pre>
       +<pre><code>	return 42;</code></pre>
     # /tmp/d20111129-16859-197z0bq/spec.rb:660:in `expect_transformation'
     # /tmp/d20111129-16859-197z0bq/spec.rb:186:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  6) Formatter code blocks renders properly with mixed content
     Failure/Error: Formatter.new(plain).to_html.should eq formatted.strip
       
       expected: "<h1>This is a header</h1>\n\n<p>Some parahraphs here</p>\n\n<pre><code>Some clean code\nWhich is also beautiful\nAnd maybe also compiles!</code></pre>\n\n<p>More paragraphs there?</p>"
            got: "<h1>This is a header</h1>\n<p>Some parahraphs here</p>\n<pre><code>Some clean codeWhich is also beautiful</code></pre>\n<pre><code>And maybe also compiles!</code></pre>\n<p>More paragraphs there?</p>"
       
       (compared using ==)
       
       Diff:
       
       @@ -1,10 +1,6 @@
        <h1>This is a header</h1>
       -
        <p>Some parahraphs here</p>
       -
       -<pre><code>Some clean code
       -Which is also beautiful
       -And maybe also compiles!</code></pre>
       -
       +<pre><code>Some clean codeWhich is also beautiful</code></pre>
       +<pre><code>And maybe also compiles!</code></pre>
        <p>More paragraphs there?</p>
     # /tmp/d20111129-16859-197z0bq/spec.rb:660:in `expect_transformation'
     # /tmp/d20111129-16859-197z0bq/spec.rb:214:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  7) Formatter blockquotes renders multiline ones
     Failure/Error: Formatter.new(plain).to_html.should eq formatted.strip
       
       expected: "<blockquote><p>First line.\nSecond line.\nThird line.</p></blockquote>"
            got: "<blockquote><p>First line.\\nSecond line.</p></blockquote>\n<blockquote><p>Third line.</p></blockquote>"
       
       (compared using ==)
       
       Diff:
       @@ -1,4 +1,3 @@
       -<blockquote><p>First line.
       -Second line.
       -Third line.</p></blockquote>
       +<blockquote><p>First line.\nSecond line.</p></blockquote>
       +<blockquote><p>Third line.</p></blockquote>
     # /tmp/d20111129-16859-197z0bq/spec.rb:660:in `expect_transformation'
     # /tmp/d20111129-16859-197z0bq/spec.rb:236:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  8) Formatter blockquotes renders multiple ones
     Failure/Error: Formatter.new(plain).to_html.should eq formatted.strip
       
       expected: "<blockquote><p>First quote.</p></blockquote>\n\n<blockquote><p>Second quote.</p></blockquote>"
            got: "<blockquote><p>First quote.\\nSecond quote.</p></blockquote>"
       
       (compared using ==)
       
       Diff:
       @@ -1,4 +1,2 @@
       -<blockquote><p>First quote.</p></blockquote>
       -
       -<blockquote><p>Second quote.</p></blockquote>
       +<blockquote><p>First quote.\nSecond quote.</p></blockquote>
     # /tmp/d20111129-16859-197z0bq/spec.rb:660:in `expect_transformation'
     # /tmp/d20111129-16859-197z0bq/spec.rb:252:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  9) Formatter blockquotes renders multiline ones with multiple paragraphs
     Failure/Error: Formatter.new(plain).to_html.should eq formatted.strip
       
       expected: "<blockquote><p>First quote.</p>\n\n<p>Second quote.</p></blockquote>"
            got: "<blockquote><p>First quote.</p></blockquote>\n<p>></p>\n<blockquote><p>Second quote.</p></blockquote>"
       
       (compared using ==)
       
       Diff:
       @@ -1,4 +1,4 @@
       -<blockquote><p>First quote.</p>
       -
       -<p>Second quote.</p></blockquote>
       +<blockquote><p>First quote.</p></blockquote>
       +<p>></p>
       +<blockquote><p>Second quote.</p></blockquote>
     # /tmp/d20111129-16859-197z0bq/spec.rb:660:in `expect_transformation'
     # /tmp/d20111129-16859-197z0bq/spec.rb:268:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  10) Formatter links allows multiple links on a single line
     Failure/Error: Formatter.new(plain).to_html.should eq formatted.strip
       
       expected: "<p>We have <a href=\"some-url\">a first</a> and <a href=\"another-url\">Second</a>.</p>"
            got: "<p>We have <a href=\"another-url\">a first](some-url) and [Second</a>.</p>"
       
       (compared using ==)
     # /tmp/d20111129-16859-197z0bq/spec.rb:660:in `expect_transformation'
     # /tmp/d20111129-16859-197z0bq/spec.rb:286:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  11) Formatter lists renders simple unordered lists
     Failure/Error: Formatter.new(plain).to_html.should eq formatted.strip
       
       expected: "<ul>\n  <li>\u0415\u0434\u043D\u043E.</li>\n  <li>\u0414\u0440\u0443\u0433\u043E.</li>\n  <li>\u0422\u0440\u0435\u0442\u043E...</li>\n</ul>"
            got: "<ul>\n  <li>\u0415\u0434\u043D\u043E.</li>\n  <li>\u0414\u0440\u0443\u0433\u043E.</li>\n</ul>\n<ul>\n  <li>\u0422\u0440\u0435\u0442\u043E...</li>\n</ul>"
       
       (compared using ==)
       
       Diff:
       @@ -1,6 +1,8 @@
        <ul>
          <li>Едно.</li>
          <li>Друго.</li>
       +</ul>
       +<ul>
          <li>Трето...</li>
        </ul>
     # /tmp/d20111129-16859-197z0bq/spec.rb:660:in `expect_transformation'
     # /tmp/d20111129-16859-197z0bq/spec.rb:330:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  12) Formatter lists renders simple ordered lists
     Failure/Error: Formatter.new(plain).to_html.should eq formatted.strip
     NoMethodError:
       undefined method `ordere_list_inner' for #<Parser:0x9d32be8>
     # /tmp/d20111129-16859-197z0bq/solution.rb:126:in `ordered_list'
     # /tmp/d20111129-16859-197z0bq/solution.rb:172:in `each'
     # /tmp/d20111129-16859-197z0bq/solution.rb:172:in `each_with_index'
     # /tmp/d20111129-16859-197z0bq/solution.rb:172:in `parse'
     # /tmp/d20111129-16859-197z0bq/solution.rb:206:in `to_html'
     # /tmp/d20111129-16859-197z0bq/spec.rb:660:in `expect_transformation'
     # /tmp/d20111129-16859-197z0bq/spec.rb:348:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  13) Formatter lists renders lists with a single item
     Failure/Error: Formatter.new(plain).to_html.should eq formatted.strip
     NoMethodError:
       undefined method `ordere_list_inner' for #<Parser:0x9dfe93c>
     # /tmp/d20111129-16859-197z0bq/solution.rb:126:in `ordered_list'
     # /tmp/d20111129-16859-197z0bq/solution.rb:172:in `each'
     # /tmp/d20111129-16859-197z0bq/solution.rb:172:in `each_with_index'
     # /tmp/d20111129-16859-197z0bq/solution.rb:172:in `parse'
     # /tmp/d20111129-16859-197z0bq/solution.rb:206:in `to_html'
     # /tmp/d20111129-16859-197z0bq/spec.rb:660:in `expect_transformation'
     # /tmp/d20111129-16859-197z0bq/spec.rb:374:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  14) Formatter lists does not choke on malformed lists
     Failure/Error: Formatter.new(plain).to_html.should eq formatted.strip
       
       expected: "<p>1) \u041F\u044A\u0440\u0432\u043E.\n2 \u0412\u0442\u043E\u0440\u043E.\n3.\u0422\u0440\u0435\u0442\u043E</p>\n<ol>\n  <li>\u0427\u0435\u0442\u0432\u044A\u0440\u0442\u043E</li>\n</ol>"
            got: "<p>1) \u041F\u044A\u0440\u0432\u043E.</p>\n\n<p>2 \u0412\u0442\u043E\u0440\u043E.</p>\n\n<p>3.\u0422\u0440\u0435\u0442\u043E</p>\n\n<p>4. \u0427\u0435\u0442\u0432\u044A\u0440\u0442\u043E</p>"
       
       (compared using ==)
       
       Diff:
       @@ -1,7 +1,8 @@
       -<p>1) Първо.
       -2 Второ.
       -3.Трето</p>
       -<ol>
       -  <li>Четвърто</li>
       -</ol>
       +<p>1) Първо.</p>
       +
       +<p>2 Второ.</p>
       +
       +<p>3.Трето</p>
       +
       +<p>4. Четвърто</p>
     # /tmp/d20111129-16859-197z0bq/spec.rb:660:in `expect_transformation'
     # /tmp/d20111129-16859-197z0bq/spec.rb:394:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  15) Formatter bold and italic text rendering does not allow parial overlapping
     Failure/Error: Formatter.new(plain).to_html.should eq formatted.strip
       
       expected: "<p>Some <em>more words **to be</em> emphasized**!</p>"
            got: "<p>Some <em>more words <strong>to be</em> emphasized</strong>!</p>"
       
       (compared using ==)
     # /tmp/d20111129-16859-197z0bq/spec.rb:660:in `expect_transformation'
     # /tmp/d20111129-16859-197z0bq/spec.rb:496:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  16) Formatter whitespace removes excess leading and trailing whitespace
     Failure/Error: Formatter.new(plain).to_html.should eq formatted.strip
       
       expected: "<p>Some txt</p>"
            got: "<p></p>\n\n<p></p>\n\n<p>Some txt</p>\n\n<p></p>\n\n<p></p>"
       
       (compared using ==)
       
       Diff:
       
       @@ -1,2 +1,10 @@
       +<p></p>
       +
       +<p></p>
       +
        <p>Some txt</p>
       +
       +<p></p>
       +
       +<p></p>
     # /tmp/d20111129-16859-197z0bq/spec.rb:660:in `expect_transformation'
     # /tmp/d20111129-16859-197z0bq/spec.rb:665:in `block in expect_transformations'
     # /tmp/d20111129-16859-197z0bq/spec.rb:664:in `each'
     # /tmp/d20111129-16859-197z0bq/spec.rb:664:in `expect_transformations'
     # /tmp/d20111129-16859-197z0bq/spec.rb:547:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  17) Formatter whitespace ignores leading and trailing whitespace of lines whenever possible
     Failure/Error: Formatter.new(plain).to_html.should eq formatted.strip
       
       expected: "<h1>Test with a header</h1>"
            got: "<p>#    Test with a header</p>"
       
       (compared using ==)
     # /tmp/d20111129-16859-197z0bq/spec.rb:660:in `expect_transformation'
     # /tmp/d20111129-16859-197z0bq/spec.rb:665:in `block in expect_transformations'
     # /tmp/d20111129-16859-197z0bq/spec.rb:664:in `each'
     # /tmp/d20111129-16859-197z0bq/spec.rb:664:in `expect_transformations'
     # /tmp/d20111129-16859-197z0bq/spec.rb:555:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  18) Formatter whitespace does not touch trailing whitespace in code blocks
     Failure/Error: Formatter.new(plain).to_html.should eq formatted.strip
       
       expected: "<pre><code>First  \n  Second  </code></pre>"
            got: "<pre><code>First    Second  </code></pre>"
       
       (compared using ==)
       
       Diff:
       @@ -1,3 +1,2 @@
       -<pre><code>First  
       -  Second  </code></pre>
       +<pre><code>First    Second  </code></pre>
     # /tmp/d20111129-16859-197z0bq/spec.rb:660:in `expect_transformation'
     # /tmp/d20111129-16859-197z0bq/spec.rb:665:in `block in expect_transformations'
     # /tmp/d20111129-16859-197z0bq/spec.rb:664:in `each'
     # /tmp/d20111129-16859-197z0bq/spec.rb:664:in `expect_transformations'
     # /tmp/d20111129-16859-197z0bq/spec.rb:563:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  19) Formatter mixed, complex input renders properly
     Failure/Error: Formatter.new(plain).to_html.should eq formatted.strip
       
       expected: "<h1>\u0426\u044F\u043B\u043E\u0441\u0442\u0435\u043D \u043F\u0440\u0438\u043C\u0435\u0440</h1>\n<p>\u0422\u0443\u043A \u0449\u0435 \u0434\u0435\u043C\u043E\u043D\u0441\u0442\u0440\u0438\u0440\u0430\u043C\u0435 \u043D\u0430\u043A\u0440\u0430\u0442\u043A\u043E \u0432\u044A\u0437\u043C\u043E\u0436\u043D\u043E\u0441\u0442\u0438\u0442\u0435 \u043D\u0430 \u043D\u0430\u0448\u0438\u044F \u043F\u0440\u043E\u0441\u0442 Markdown-\u043F\u0440\u0435\u043E\u0431\u0440\u0430\u0437\u0443\u0432\u0430\u0442\u0435\u043B.</p>\n\n<h2><em>\u0424\u0438\u043B\u043E\u0441\u043E\u0444\u0438\u044F</em> \u043D\u0430 <a href=\"http://daringfireball.net/projects/markdown/syntax#philosophy\">Markdown</a></h2>\n\n<p>\u041A\u0440\u0430\u0442\u044A\u043A \u0446\u0438\u0442\u0430\u0442 \u043E\u0442\u043D\u043E\u0441\u043D\u043E \u0444\u0438\u043B\u043E\u0441\u043E\u0444\u0438\u044F\u0442\u0430 \u043D\u0430 Markdown:</p>\n<blockquote><p>Markdown is intended to be as easy-to-read and easy-to-write as is feasible.</p>\n\n<p>Readability, however, is emphasized above all else. A Markdown-formatted document should be publishable as-is, as plain text, without looking like it\u2019s been marked up with tags or formatting instructions.</p></blockquote>\n<p>\u041F\u043E\u0432\u0435\u0447\u0435 \u0438\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044F \u043C\u043E\u0436\u0435 \u0434\u0430 \u043D\u0430\u043C\u0435\u0440\u0438\u0442\u0435 \u043D\u0430 <a href=\"http://daringfireball.net/projects/markdown\">\u0441\u0430\u0439\u0442\u0430 \u043D\u0430 <strong>Markdown</strong></a>.</p>\n\n<h2>\u041F\u0440\u0435\u0434\u0438\u043C\u0441\u0442\u0432\u0430</h2>\n\n<p>\u0421\u044A\u0437\u0434\u0430\u0432\u0430\u043D\u0435\u0442\u043E \u043D\u0430 \u0441\u044A\u0434\u044A\u0440\u0436\u0430\u043D\u0438\u0435 \u0432\u044A\u0432 \u0444\u043E\u0440\u043C\u0430\u0442\u0430 Markdown \u0438\u043C\u0430 \u043C\u043D\u043E\u0436\u0435\u0441\u0442\u0432\u043E \u043F\u0440\u0435\u0434\u0438\u043C\u0441\u0442\u0432\u0430.</p>\n\n<p>\u0415\u0442\u043E \u043D\u044F\u043A\u043E\u0438 \u043E\u0442 \u0442\u044F\u0445:</p>\n<ul>\n  <li>\u041B\u0435\u0441\u043D\u043E \u0447\u0435\u0442\u0438\u043C \u0432 <em>\u0441\u0443\u0440\u043E\u0432</em> \u0432\u0438\u0434</li>\n  <li>\u0411\u0435\u0437 &quot;\u0441\u043A\u0440\u0438\u0442\u0438&quot; \u0444\u043E\u0440\u043C\u0430\u0442\u0438\u0440\u0430\u0449\u0438 \u0442\u0430\u0433\u043E\u0432\u0435 \u2014 \u0444\u043E\u0440\u043C\u0430\u0442\u0438\u0440\u0430\u043D\u0435\u0442\u043E \u0432\u0438 \u043D\u0438\u043A\u043E\u0433\u0430 \u043D\u0435 \u0441\u0435 \u0447\u0443\u043F\u0438</li>\n  <li>\u0421\u043B\u0435\u0434 \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u043A\u0430 \u043C\u043E\u0436\u0435 \u0434\u0430 \u0438\u0437\u0433\u043B\u0435\u0436\u0434\u0430 \u043C\u043D\u043E\u0433\u043E \u0434\u043E\u0431\u0440\u0435</li>\n</ul>\n\n<h2>\u041F\u043E\u0434\u0434\u0440\u044A\u0436\u043A\u0430 \u0432 <em>Ruby</em></h2>\n\n<p>\u0412 <strong>Ruby</strong> \u0438\u043C\u0430 \u043C\u043D\u043E\u0436\u0435\u0441\u0442\u0432\u043E Gem-\u043E\u0432\u0435, \u043A\u043E\u0438\u0442\u043E \u043C\u043E\u0433\u0430\u0442 \u0434\u0430 \u0432\u0438 \u043F\u043E\u043C\u043E\u0433\u043D\u0430\u0442 \u0437\u0430 \u0434\u0430 \u043F\u0440\u0435\u0445\u0432\u044A\u0440\u043B\u044F\u0442\u0435 Markdown-\u0441\u044A\u0434\u044A\u0440\u0436\u0430\u043D\u0438\u0435 \u0432 HTML-\u0444\u043E\u0440\u043C\u0430\u0442.\n\u041A\u043E\u0434\u044A\u0442, \u043A\u043E\u0439\u0442\u043E \u0432\u0438\u0435 \u0441\u044A\u0437\u0434\u0430\u0432\u0430\u0442\u0435, \u0441\u044A\u0449\u043E \u043C\u043E\u0436\u0435 \u0434\u0430 \u0432\u044A\u0440\u0448\u0438 \u0442\u043E\u0432\u0430 \u0434\u043E \u0438\u0437\u0432\u0435\u0441\u0442\u043D\u0430 \u0441\u0442\u0435\u043F\u0435\u043D.</p>\n\n<p>\u041F\u0440\u0438\u043C\u0435\u0440 \u0437\u0430 \u0443\u043F\u043E\u0442\u0440\u0435\u0431\u0430 \u043D\u0430 \u0432\u0430\u0448\u0438\u044F \u043A\u043E\u0434:</p>\n\n<pre><code># \u041C\u043D\u043E\u0433\u043E \u043F\u0440\u043E\u0441\u0442\u043E\nformatter = Formatter.new &quot;## My Markdown&quot;\nputs formatter.to_html</code></pre>"
            got: "<h1>\u0426\u044F\u043B\u043E\u0441\u0442\u0435\u043D \u043F\u0440\u0438\u043C\u0435\u0440</h1>\n<p>\u0422\u0443\u043A \u0449\u0435 \u0434\u0435\u043C\u043E\u043D\u0441\u0442\u0440\u0438\u0440\u0430\u043C\u0435 \u043D\u0430\u043A\u0440\u0430\u0442\u043A\u043E \u0432\u044A\u0437\u043C\u043E\u0436\u043D\u043E\u0441\u0442\u0438\u0442\u0435 \u043D\u0430 \u043D\u0430\u0448\u0438\u044F \u043F\u0440\u043E\u0441\u0442 Markdown-\u043F\u0440\u0435\u043E\u0431\u0440\u0430\u0437\u0443\u0432\u0430\u0442\u0435\u043B.</p>\n<h2><em>\u0424\u0438\u043B\u043E\u0441\u043E\u0444\u0438\u044F</em> \u043D\u0430 <a href=\"http://daringfireball.net/projects/markdown/syntax#philosophy\">Markdown</a></h2>\n<p>\u041A\u0440\u0430\u0442\u044A\u043A \u0446\u0438\u0442\u0430\u0442 \u043E\u0442\u043D\u043E\u0441\u043D\u043E \u0444\u0438\u043B\u043E\u0441\u043E\u0444\u0438\u044F\u0442\u0430 \u043D\u0430 Markdown:</p>\n<blockquote><p>Markdown is intended to be as easy-to-read and easy-to-write as is feasible.</p></blockquote>\n<p>></p>\n<blockquote><p>Readability, however, is emphasized above all else. A Markdown-formatted document should be publishable as-is, as plain text, without looking like it\u2019s been marked up with tags or formatting instructions.</p></blockquote>\n<p>\u041F\u043E\u0432\u0435\u0447\u0435 \u0438\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044F \u043C\u043E\u0436\u0435 \u0434\u0430 \u043D\u0430\u043C\u0435\u0440\u0438\u0442\u0435 \u043D\u0430 <a href=\"http://daringfireball.net/projects/markdown\">\u0441\u0430\u0439\u0442\u0430 \u043D\u0430 <strong>Markdown</strong></a>.</p>\n<h2>\u041F\u0440\u0435\u0434\u0438\u043C\u0441\u0442\u0432\u0430</h2>\n<p>\u0421\u044A\u0437\u0434\u0430\u0432\u0430\u043D\u0435\u0442\u043E \u043D\u0430 \u0441\u044A\u0434\u044A\u0440\u0436\u0430\u043D\u0438\u0435 \u0432\u044A\u0432 \u0444\u043E\u0440\u043C\u0430\u0442\u0430 Markdown \u0438\u043C\u0430 \u043C\u043D\u043E\u0436\u0435\u0441\u0442\u0432\u043E \u043F\u0440\u0435\u0434\u0438\u043C\u0441\u0442\u0432\u0430.</p>\n\n<p>\u0415\u0442\u043E \u043D\u044F\u043A\u043E\u0438 \u043E\u0442 \u0442\u044F\u0445:</p>\n<ul>\n  <li>\u041B\u0435\u0441\u043D\u043E \u0447\u0435\u0442\u0438\u043C \u0432 <em>\u0441\u0443\u0440\u043E\u0432</em> \u0432\u0438\u0434</li>\n  <li>\u0411\u0435\u0437 &quot;\u0441\u043A\u0440\u0438\u0442\u0438&quot; \u0444\u043E\u0440\u043C\u0430\u0442\u0438\u0440\u0430\u0449\u0438 \u0442\u0430\u0433\u043E\u0432\u0435 \u2014 \u0444\u043E\u0440\u043C\u0430\u0442\u0438\u0440\u0430\u043D\u0435\u0442\u043E \u0432\u0438 \u043D\u0438\u043A\u043E\u0433\u0430 \u043D\u0435 \u0441\u0435 \u0447\u0443\u043F\u0438</li>\n</ul>\n<ul>\n  <li>\u0421\u043B\u0435\u0434 \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u043A\u0430 \u043C\u043E\u0436\u0435 \u0434\u0430 \u0438\u0437\u0433\u043B\u0435\u0436\u0434\u0430 \u043C\u043D\u043E\u0433\u043E \u0434\u043E\u0431\u0440\u0435</li>\n</ul>\n<h2>\u041F\u043E\u0434\u0434\u0440\u044A\u0436\u043A\u0430 \u0432 <em>Ruby</em></h2>\n<p>\u0412 <strong>Ruby</strong> \u0438\u043C\u0430 \u043C\u043D\u043E\u0436\u0435\u0441\u0442\u0432\u043E Gem-\u043E\u0432\u0435, \u043A\u043E\u0438\u0442\u043E \u043C\u043E\u0433\u0430\u0442 \u0434\u0430 \u0432\u0438 \u043F\u043E\u043C\u043E\u0433\u043D\u0430\u0442 \u0437\u0430 \u0434\u0430 \u043F\u0440\u0435\u0445\u0432\u044A\u0440\u043B\u044F\u0442\u0435 Markdown-\u0441\u044A\u0434\u044A\u0440\u0436\u0430\u043D\u0438\u0435 \u0432 HTML-\u0444\u043E\u0440\u043C\u0430\u0442.</p>\n\n<p>\u041A\u043E\u0434\u044A\u0442, \u043A\u043E\u0439\u0442\u043E \u0432\u0438\u0435 \u0441\u044A\u0437\u0434\u0430\u0432\u0430\u0442\u0435, \u0441\u044A\u0449\u043E \u043C\u043E\u0436\u0435 \u0434\u0430 \u0432\u044A\u0440\u0448\u0438 \u0442\u043E\u0432\u0430 \u0434\u043E \u0438\u0437\u0432\u0435\u0441\u0442\u043D\u0430 \u0441\u0442\u0435\u043F\u0435\u043D.</p>\n\n<p>\u041F\u0440\u0438\u043C\u0435\u0440 \u0437\u0430 \u0443\u043F\u043E\u0442\u0440\u0435\u0431\u0430 \u043D\u0430 \u0432\u0430\u0448\u0438\u044F \u043A\u043E\u0434:</p>\n<pre><code># \u041C\u043D\u043E\u0433\u043E \u043F\u0440\u043E\u0441\u0442\u043Eformatter = Formatter.new &quot;## My Markdown&quot;</code></pre>\n<pre><code>puts formatter.to_html</code></pre>"
       
       (compared using ==)
       
       Diff:
       
       
       
       
       
       
       
       
       
       @@ -1,33 +1,28 @@
        <h1>Цялостен пример</h1>
        <p>Тук ще демонстрираме накратко възможностите на нашия прост Markdown-преобразувател.</p>
       -
        <h2><em>Философия</em> на <a href="http://daringfireball.net/projects/markdown/syntax#philosophy">Markdown</a></h2>
       -
        <p>Кратък цитат относно философията на Markdown:</p>
       -<blockquote><p>Markdown is intended to be as easy-to-read and easy-to-write as is feasible.</p>
       -
       -<p>Readability, however, is emphasized above all else. A Markdown-formatted document should be publishable as-is, as plain text, without looking like it’s been marked up with tags or formatting instructions.</p></blockquote>
       +<blockquote><p>Markdown is intended to be as easy-to-read and easy-to-write as is feasible.</p></blockquote>
       +<p>></p>
       +<blockquote><p>Readability, however, is emphasized above all else. A Markdown-formatted document should be publishable as-is, as plain text, without looking like it’s been marked up with tags or formatting instructions.</p></blockquote>
        <p>Повече информация може да намерите на <a href="http://daringfireball.net/projects/markdown">сайта на <strong>Markdown</strong></a>.</p>
       -
        <h2>Предимства</h2>
       -
        <p>Създаването на съдържание във формата Markdown има множество предимства.</p>
        
        <p>Ето някои от тях:</p>
        <ul>
          <li>Лесно четим в <em>суров</em> вид</li>
          <li>Без &quot;скрити&quot; форматиращи тагове — форматирането ви никога не се чупи</li>
       +</ul>
       +<ul>
          <li>След обработка може да изглежда много добре</li>
        </ul>
       -
        <h2>Поддръжка в <em>Ruby</em></h2>
       +<p>В <strong>Ruby</strong> има множество Gem-ове, които могат да ви помогнат за да прехвърляте Markdown-съдържание в HTML-формат.</p>
        
       -<p>В <strong>Ruby</strong> има множество Gem-ове, които могат да ви помогнат за да прехвърляте Markdown-съдържание в HTML-формат.
       -Кодът, който вие създавате, също може да върши това до известна степен.</p>
       +<p>Кодът, който вие създавате, също може да върши това до известна степен.</p>
        
        <p>Пример за употреба на вашия код:</p>
       -
       -<pre><code># Много просто
       -formatter = Formatter.new &quot;## My Markdown&quot;
       -puts formatter.to_html</code></pre>
       +<pre><code># Много простоformatter = Formatter.new &quot;## My Markdown&quot;</code></pre>
       +<pre><code>puts formatter.to_html</code></pre>
     # /tmp/d20111129-16859-197z0bq/spec.rb:660:in `expect_transformation'
     # /tmp/d20111129-16859-197z0bq/spec.rb:655:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

Finished in 0.61993 seconds
57 examples, 19 failures

Failed examples:

rspec /tmp/d20111129-16859-197z0bq/spec.rb:28 # Formatter paragraphs renders multiline paragraps
rspec /tmp/d20111129-16859-197z0bq/spec.rb:99 # Formatter headers renders tricky ones
rspec /tmp/d20111129-16859-197z0bq/spec.rb:127 # Formatter code blocks renders multiline blocks with empty lines
rspec /tmp/d20111129-16859-197z0bq/spec.rb:143 # Formatter code blocks renders multiple ones
rspec /tmp/d20111129-16859-197z0bq/spec.rb:163 # Formatter code blocks renders properly a longer example with tabs and Unicode
rspec /tmp/d20111129-16859-197z0bq/spec.rb:189 # Formatter code blocks renders properly with mixed content
rspec /tmp/d20111129-16859-197z0bq/spec.rb:223 # Formatter blockquotes renders multiline ones
rspec /tmp/d20111129-16859-197z0bq/spec.rb:239 # Formatter blockquotes renders multiple ones
rspec /tmp/d20111129-16859-197z0bq/spec.rb:255 # Formatter blockquotes renders multiline ones with multiple paragraphs
rspec /tmp/d20111129-16859-197z0bq/spec.rb:285 # Formatter links allows multiple links on a single line
rspec /tmp/d20111129-16859-197z0bq/spec.rb:315 # Formatter lists renders simple unordered lists
rspec /tmp/d20111129-16859-197z0bq/spec.rb:333 # Formatter lists renders simple ordered lists
rspec /tmp/d20111129-16859-197z0bq/spec.rb:351 # Formatter lists renders lists with a single item
rspec /tmp/d20111129-16859-197z0bq/spec.rb:377 # Formatter lists does not choke on malformed lists
rspec /tmp/d20111129-16859-197z0bq/spec.rb:495 # Formatter bold and italic text rendering does not allow parial overlapping
rspec /tmp/d20111129-16859-197z0bq/spec.rb:541 # Formatter whitespace removes excess leading and trailing whitespace
rspec /tmp/d20111129-16859-197z0bq/spec.rb:550 # Formatter whitespace ignores leading and trailing whitespace of lines whenever possible
rspec /tmp/d20111129-16859-197z0bq/spec.rb:558 # Formatter whitespace does not touch trailing whitespace in code blocks
rspec /tmp/d20111129-16859-197z0bq/spec.rb:586 # Formatter mixed, complex input renders properly

История (1 версия и 0 коментара)

Георги обнови решението на 23.11.2011 19:16 (преди над 12 години)

+module Element
+ class BaseElement
+ attr_reader :text
+
+ def initialize(text)
+ @text = [text]
+ end
+
+ def add_line(line)
+ @text = @text.concat(Array.new(line))
+ end
+
+ def strip
+ @text.map!(&:strip)
+ end
+
+ def inlineformatting
+ @text.map! do |line|
+ line.gsub(/\[{1}(?<text>.+)\]{1}\({1}(?<href>.+)\){1}/,
+ '<a href="\k<href>">\k<text></a>').
+ gsub(/\_{1}(?<text>.+?)\_{1}/, '<em>\k<text></em>').
+ gsub(/\*{2}(?<text>.+?)\*{2}/, '<strong>\k<text></strong>')
+ end
+ end
+
+ alias to_html to_s
+
+ def to_html
+ strip
+ inlineformatting
+ @text.join('\n')
+ end
+ end
+
+ class Heading1 < BaseElement
+ def to_html
+ "<h1>#{super.strip}</h1>"
+ end
+ end
+
+ class Heading2 < BaseElement
+ def to_html
+ "<h2>#{super.strip}</h2>"
+ end
+ end
+
+ class Heading3 < BaseElement
+ def to_html
+ "<h3>#{super.strip}</h3>"
+ end
+ end
+
+ class Heading4 < BaseElement
+ def to_html
+ "<h4>#{super.strip}</h4>"
+ end
+ end
+
+ class Code < BaseElement
+ def to_html
+ "<pre><code>#{@text.join}</code></pre>"
+ end
+ end
+
+ class Paragraph < BaseElement
+ def to_html
+ "<p>#{super}</p>"
+ end
+ end
+
+ class BlockQuote < BaseElement
+ def to_html
+ "<blockquote><p>#{super}</p></blockquote>"
+ end
+ end
+
+ class OrderedList < BaseElement
+ def to_html
+ separator = "</li>\n <li>"
+ "<ol>\n <li>#{super.split('\n').join(separator)}</li>\n</ol>"
+ end
+ end
+
+ class UnorderedList < BaseElement
+ def to_html
+ separator = "</li>\n <li>"
+ "<ul>\n <li>#{super.split('\n').join(separator)}</li>\n</ul>"
+ end
+ end
+end
+
+class Parser
+ attr_reader :lines
+
+ def initialize(text)
+ @lines = text.split("\n")
+ end
+
+ @@line_patterns =
+ { Element::Heading1 => /^\#{1}\s+(?<text>\S+.+?)$/,
+ Element::Heading2 => /^\#{2}\s+(?<text>\S+.+?)$/,
+ Element::Heading3 => /^\#{3}\s+(?<text>\S+.+?)$/,
+ Element::Heading4 => /^\#{4}\s+(?<text>\S+.+?)$/,
+ Element::Code => /^\s{4}(?<text>.+)$/, Element::BlockQuote => /^>\s+(?<text>.+?)$/,
+ Element::UnorderedList => /^\*{1}\s{1}(?<text>.+)$/
+ }
+
+ @@ordered_list_pattern = /^(?<number>\d+)\.\s(?<text>.+)$/
+
+ def simple_parsing(line)
+ return line unless line.is_a? String
+ @@line_patterns.each do |k,v|
+ match = line.match(v)
+ return k.new(match[:text]) if match and match[:text]
+ end
+ line
+ end
+
+ def ordered_list(line, index)
+ return unless line.is_a? String
+
+ match = line.match @@ordered_list_pattern
+ return unless (match != nil and match[:number].to_i == 1)
+ @lines[index] = Element::OrderedList.new(match[:text])
+
+ ordere_list_inner(index)
+ end
+
+ def ordered_list_inner(parent_idx)
+ lines_to_delete = []
+ (parent_idx+1..@lines.length).each do |i|
+ next unless @lines[i].is_a? String
+ succ_match = @lines[i].match @@ordered_list_pattern
+ break unless succ_match and succ_match[:number].to_i == 1+i-parent_idx
+
+ @lines[parent_idx].add_line(succ_match[:text])
+ lines_to_delete << i
+ end
+ delete_line lines_to_delete
+ end
+
+ def delete_line lines
+ Array.new(lines).each do |i|
+ @lines.delete_at i
+ end
+ end
+
+ def make_paragraphs(line)
+ return line unless line.is_a? String
+ Element::Paragraph.new(line)
+ end
+
+ def join_multiline(lines)
+ (0...lines.length).each do |i|
+ next unless [Element::UnorderedList, Element::Code, Element::BlockQuote].
+ index(lines[i].class)
+ join_multiline_inner(lines, i)
+ end
+ lines
+ end
+
+ def join_multiline_inner(lines, parent_idx)
+ (parent_idx+1...lines.length).each do |i|
+ return unless (lines[i].is_a? lines[parent_idx].class)
+ lines[parent_idx].add_line lines[i].text
+ lines.delete_at i
+ end
+ end
+
+ def parse
+ @lines.map!(&method(:simple_parsing))
+ @lines.each_with_index(&method(:ordered_list))
+ @lines.reject! {|line| line.is_a? String and line.match(/^$/)}
+ @lines.map!(&method(:make_paragraphs))
+ @lines = join_multiline @lines
+ @lines
+ end
+end
+
+class SpecialCharacters
+ def self.substitute text
+ text.gsub(/\&/, "&amp;").
+ gsub(/(?<before>.+)\>/, '\k<before>&gt;').
+ gsub(/\</, "&lt;").
+ gsub(/\"/, "&quot;")
+ end
+end
+
+class OutputGenerator
+ def self.generate_markdown(lines)
+ lines.map(&:to_html).join("\n").gsub(/<\/p>\n<p>/,"</p>\n\n<p>").strip
+ end
+end
+
+class Formatter
+ def initialize(text)
+ @text = text
+ end
+
+ def to_s
+ to_html
+ end
+
+ def to_html
+ parser = Parser.new(SpecialCharacters.substitute(@text))
+ OutputGenerator.generate_markdown(parser.parse)
+ end
+
+ def inspect
+ @text
+ end
+end