This solution:
content_tag(:ul) do [1, 2].map do |x| content_tag(:li, x) << content_tag(:ul) do ['a', 'b'].map do |y| content_tag(:li, y) << content_tag(:ul) do ['i', 'ii'].map do |z| content_tag(:li, z) end.join('').html_safe end end.join('').html_safe end end.join('').html_safe end
The problem is that you have to return a safe HTML string at the end of each content_tag block:
# returns <li>asd</li> content_tag('li') { 'asd' } # returns <ul><li>asd</li></ul> content_tag('ul') { content_tag('li') { 'asd' } } # returns [1, 2] [1, 2].each{ |x| content_tag('li', x) } # returns ["<li>1</li>", "<li>2</li>"] [1, 2].map{ |x| content_tag('li', x) } # returns "<li>1</li><li>2</li>" not marked as HTML safe, # because `join` generates a new string [1, 2].map{ |x| content_tag('li', x) }.join('') # returns "<li>1</li><li>2</li>" marked as HTML safe [1, 2].map{ |x| content_tag('li', x) }.join('').html_safe # returns "<ul><li>1</li><li>2</li></ul>" content_tag('ul') do [1, 2].map{ |x| content_tag('li', x) }.join('').html_safe end # returns "<ul><li>1</li><li>2</li></ul>" content_tag('ul') do content_tag('li') { 'asd' } # this is ignored, since it is not returned [1, 2].map{ |x| content_tag('li', x) }.join('').html_safe end # returns "<ul><li>asd</li><li>1</li><li>2</li></ul>" content_tag('ul') do content_tag('li') { 'asd' } << [1, 2].map{ |x| content_tag('li', x) }.join('').html_safe end
source share