Решение на Пета задача от Иван Арабаджиев

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

Към профила на Иван Арабаджиев

Резултати

  • 8 точки от тестове
  • 0 бонус точки
  • 8 точки общо
  • 56 успешни тест(а)
  • 1 неуспешни тест(а)

Код

# Known issues (feeling sick, might break something if I don`t stop)
# quote/whitespace handling is ugly as hell
class Formatter #not completely implemented yet
def initialize(raw_markdown)
@raw_markdown = raw_markdown
end
def to_html
chopped = @raw_markdown.chomp
chopped.strip! unless chopped.start_with? ' '
bloated = paragraphs(code_segments(white_space(chopped))).chomp
bloated.gsub(/[ ]+(<\/?p>)/u, '\1').gsub(/^[ ]{4}/u, '')
end
alias to_s to_html
def white_space(raw_text)
corrected = raw_text.gsub(/(?<=^)[ ]{,3}(\S.+?)[ ]*(?=$)/u, '\1')
corrected = corrected.gsub(/^[ ]{,3}(\S[^\n]+)[ ]*([\n$]+)/u,'\1\2')
#next, we predictably break blockquotes!
corrected.gsub!(/^((>[ ]+[^\n]*[\n]??)\g<2>*?)([\n]{2}|[\n][^>]|\Z)/um,
"<blockquote>\n\\1\n</blockquote>\\3")
corrected.gsub! /(^)[>][ ]+/, '\1' #and help paragraph wrapping
corrected
end
def paragraphs(raw_string) #processes every line not starting with [ <]
done = raw_string.gsub(/^([#]{1,4})[ ]+(\S.*)$/u) do
process_simple_tags "<h#{$1.length}>#{escape_specials $2}</h#{$1.length}>"
end
done.gsub!(/^(([^ <\n][^\n]+[\n]??)\g<2>*?)([\n]{2}|[\n][\Z<]|\Z)/um) do
par = "<p>#{escape_specials $1}</p>#{$3}"
par = process_simple_tags par
end
done.gsub("<blockquote>\n", '<blockquote>').gsub("\n</blockquote>",'</blockquote>')
end
def code_segments(bare_code_string)
code = bare_code_string
code.gsub!(/([\n]{2,}|^)(^\s{4}(.*?))([\n][\S\n]|\Z)/um) do
"#{$1}<pre><code>#{escape_specials $3}</code></pre>#{$4}"
end
code.gsub!(/^((\*[ ][^\n]+[\n\Z]?)\g<1>*)/um) { "<ul>\n#{$1.chomp}\n</ul>\n" }
code.gsub!(/^((\d+\.[ ][^\n]+[\n\Z]?)\g<1>*)/um) { "<ol>\n#{$1.chomp}\n</ol>\n" }
code.gsub!(/^(\*|\d+\.)[ ](.+)$/u) do
process_simple_tags " <li>#{escape_specials $2}</li>"
end
code
end
def process_simple_tags(from='')
processed = from.gsub(/([^\[]*)\[([^\]]+)\]\(([^)]+)\)(.*)/u,
'\1<a href="\3">\2</a>\4')
processed = recurse_beutification processed
processed
end
def recurse_beutification(ugly_version)
ugly_version.gsub(/(?<!\w)(_|\*{2})(.+?)\1(?!\w)/u) { strong_or_em $~ }
end
def strong_or_em(matchdata)
case matchdata[1]
when '_' then "<em>#{recurse_beutification matchdata[2]}</em>"
when '**' then "<strong>#{recurse_beutification matchdata[2]}</strong>"
else matchdata[2]
end
end
def escape_specials(from='')
from.gsub(/&/, '&amp;').gsub(/</, '&lt;').gsub(/(?<!^)>/, '&gt;').gsub(/"/, '&quot;')
end
def inspect
@raw_markdown
end
end

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

.......................F.................................

Failures:

  1) 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=\"some-url\">a first</a> and [Second](another-url).</p>"
       
       (compared using ==)
     # /tmp/d20111129-16859-10h5hrs/spec.rb:660:in `expect_transformation'
     # /tmp/d20111129-16859-10h5hrs/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)>'

Finished in 0.7231 seconds
57 examples, 1 failure

Failed examples:

rspec /tmp/d20111129-16859-10h5hrs/spec.rb:285 # Formatter links allows multiple links on a single line

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

Иван обнови решението на 23.11.2011 00:01 (преди над 12 години)

+# Known issues (feeling sick, might break something if I don`t stop)
+# quote/whitespace handling is ugly as hell
+
+class Formatter #not completely implemented yet
+ def initialize(raw_markdown)
+ @raw_markdown = raw_markdown
+ end
+
+ def to_html
+ chopped = @raw_markdown.chomp
+ chopped.strip! unless chopped.start_with? ' '
+ bloated = paragraphs(code_segments(white_space(chopped))).chomp
+
+ bloated.gsub(/[ ]+(<\/?p>)/u, '\1').gsub(/^[ ]{4}/u, '')
+ end
+
+ alias to_s to_html
+
+ def white_space(raw_text)
+ corrected = raw_text.gsub(/(?<=^)[ ]{,3}(\S.+?)[ ]*(?=$)/u, '\1')
+ corrected = corrected.gsub(/^[ ]{,3}(\S[^\n]+)[ ]*([\n$]+)/u,'\1\2')
+ #next, we predictably break blockquotes!
+ corrected.gsub!(/^((>[ ]+[^\n]*[\n]??)\g<2>*?)([\n]{2}|[\n][^>]|\Z)/um,
+ "<blockquote>\n\\1\n</blockquote>\\3")
+ corrected.gsub! /(^)[>][ ]+/, '\1' #and help paragraph wrapping
+
+ corrected
+ end
+
+ def paragraphs(raw_string) #processes every line not starting with [ <]
+ done = raw_string.gsub(/^([#]{1,4})[ ]+(\S.*)$/u) do
+ process_simple_tags "<h#{$1.length}>#{escape_specials $2}</h#{$1.length}>"
+ end
+
+ done.gsub!(/^(([^ <\n][^\n]+[\n]??)\g<2>*?)([\n]{2}|[\n][\Z<]|\Z)/um) do
+ par = "<p>#{escape_specials $1}</p>#{$3}"
+ par = process_simple_tags par
+ end
+
+ done.gsub("<blockquote>\n", '<blockquote>').gsub("\n</blockquote>",'</blockquote>')
+ end
+
+ def code_segments(bare_code_string)
+ code = bare_code_string
+ code.gsub!(/([\n]{2,}|^)(^\s{4}(.*?))([\n][\S\n]|\Z)/um) do
+ "#{$1}<pre><code>#{escape_specials $3}</code></pre>#{$4}"
+ end
+ code.gsub!(/^((\*[ ][^\n]+[\n\Z]?)\g<1>*)/um) { "<ul>\n#{$1.chomp}\n</ul>\n" }
+ code.gsub!(/^((\d+\.[ ][^\n]+[\n\Z]?)\g<1>*)/um) { "<ol>\n#{$1.chomp}\n</ol>\n" }
+ code.gsub!(/^(\*|\d+\.)[ ](.+)$/u) do
+ process_simple_tags " <li>#{escape_specials $2}</li>"
+ end
+
+ code
+ end
+
+ def process_simple_tags(from='')
+ processed = from.gsub(/([^\[]*)\[([^\]]+)\]\(([^)]+)\)(.*)/u,
+ '\1<a href="\3">\2</a>\4')
+ processed = recurse_beutification processed
+
+ processed
+ end
+
+ def recurse_beutification(ugly_version)
+ ugly_version.gsub(/(?<!\w)(_|\*{2})(.+?)\1(?!\w)/u) { strong_or_em $~ }
+ end
+
+ def strong_or_em(matchdata)
+ case matchdata[1]
+ when '_' then "<em>#{recurse_beutification matchdata[2]}</em>"
+ when '**' then "<strong>#{recurse_beutification matchdata[2]}</strong>"
+ else matchdata[2]
+ end
+ end
+
+ def escape_specials(from='')
+ from.gsub(/&/, '&amp;').gsub(/</, '&lt;').gsub(/(?<!^)>/, '&gt;').gsub(/"/, '&quot;')
+ end
+
+ def inspect
+ @raw_markdown
+ end
+end

Проблемът ти с линковете е в регулярния израз, не в String#gsub.

Последната част на шаблона ти е (.*), която ти "изяжда" останалия текст още при първия опит за match и за втори опит за match не остава текст. Това парче е излишно и трябва да го махнеш. По същата логика, излишно парче от шаблона е и водещото ([^\[]*).

Като ги махнеш, изразът, с който заменяш, ще стане:

'<a href="\2">\1</a>'

Мдаа ... в момента като го гледам не мога да се сетя каква ми беше идеята да изглежда по точно този начин. Възможно е да съм го оставил така от някоя предишна ревизия (в която е имало смисъл), но определено края е трябвало да е non-greedy ...

Мерси много, ще мога да спя спокойно като му дойде времето :)