将ruby哈希变成html列表

时间:2011-01-03 20:43:25

标签: ruby-on-rails ruby hash

我正在尝试解析这样的yaml文件:

a:
 a1:
 a2:
b:
 b1:
  b11:
 b2:

我得到这样的哈希:

{"a"=>{"a1"=>nil, "a2"=>nil}, "b"=>{"b1"=>{"b11"=>nil}, "b2"=>nil}}

我想把它变成一个列表:

%ul
 %li a
  %ul 
   %li a1
   %li a2
 %li b
  %ul
   %li b1
    %ul
     %li b11
   %li b2

我正在尝试以最有效的方式搜索并不重要哈希的深度

最后我这样做了:

KeyWords = %w(url)

# Convert a multilevel hash into haml multilevel tree
# Special KeyWords
# url : item url
def hash_to_haml(hash, url = nil)
  haml_tag(:ul) do
    hash.each do |key, value|

      unless KeyWords.include?(key)
        url = get_url(key, value)

        haml_tag(:li) do
          haml_tag(:a, :href => url ) do
            haml_concat(key)
          end
          hash_to_haml(value) if value.is_a?(Hash) && !value.empty?
        end

      end

    end
  end
end

private

def get_url(key, hash)
  # TODO: get full url from hash
  if hash.nil?
    "/#{key}"
  else
    hash.include?("url") ? hash.delete("url") : "/#{key}"
  end
end

现在也准备解析选项了。

4 个答案:

答案 0 :(得分:4)

要输出到纯HTML,您只需在each块中执行相同函数的递归调用(或使用函数作为 each块,因为我有在这里完成):

def hash_to_html key,value
   if value.nil?
     puts "<li>#{key}</li>"
   elsif value.is_a?(Hash)
     puts "<li>#{key}"
     puts "<ul>"
     value.each(&method(:hash_to_html))
     puts "</ul></li>"
   else
     fail "I don't know what to do with a #{value.class}"
   end
end

puts "<ul>"
yourhash.each(&method(:hash_to_html))
puts "</ul>"

要输出你正在使用的任何模板语言(HAML,我认为),我们需要跟踪缩进,所以事情稍微复杂一点 - 我们将使用一个采用缩进深度的函数作为参数,并返回另一个要在每个键/值对上调用的函数,该键/值对使用适当的缩进打印该键/值对(递归)。 (在函数式编程中,以这种方式调用函数称为“部分应用函数”,它们通常比在Ruy中更容易定义。)

def hash_to_haml depth
   lambda do |key,value|
     puts " "*depth + "%li #{key}"
      if value.nil?
        # do nothing
        # (single this case out, so as not to raise an error here)
      elsif value.is_a?(Hash)
        puts " "*(depth+1) + "%ul"
        value.each(&hash_to_haml(depth+2))
      else
        fail "I don't know what to do with a #{value.class}"
      end
   end
end

puts "%ul"
yourhash.each(&hash_to_haml(1))

答案 1 :(得分:3)

我做过这个:

INDENT = '  ' # use 2 spaces for indentation

def hash_to_haml(hash, level=0)
  result = [ "#{INDENT * level}%ul" ]
  hash.each do |key,value|
    result << "#{INDENT * (level + 1)}%li #{key}"
    result << hash_to_haml(value, level + 2) if value.is_a?(Hash)
  end
  result.join("\n")
end

用法:

hash = {"a"=>{"a1"=>nil, "a2"=>nil}, "b"=>{"b1"=>{"b11"=>nil}, "b2"=>nil}
string = hash_to_haml(hash)
puts string

输出:

%ul
  %li a
    %ul
      %li a1
      %li a2
  %li b
    %ul
      %li b1
        %ul
          %li b11
      %li b2

编辑 - 澄清:

  • string * n重复string n次。
  • 使用array.join比创建一堆字符串并使用+加入它们更有效。

答案 2 :(得分:2)

require 'yaml'
yaml = <<EOS
a:
 a1:
 a2:
b:
 b1:
  b11:
 b2:
EOS

# unless using ruby 1.9 make sure you use an ordered hash such as ActiveSupport::OrderedHash 
hash = YAML::load yaml

def hash_to_haml(hash, indent)
  puts " " * indent + "%ul"
  indent += 1
  hash.each do |key, value|
    puts " " * indent + "%li " + key 
    hash_to_haml(value, indent + 1) if value.is_a? Hash
  end
end

#start with 0 indent
hash_to_haml(hash, 0)

答案 3 :(得分:1)

这是一个同时兼顾HTML和Haml的解决方案。略显冗长,但又易读。

class ListMaker
  def initialize(hash)
    @hash = hash
    @indent = "  "
    @level = 0
    @out = []
  end

  def append(tag,value=nil)
    str = @indent * @level + "#{tag}"
    str += @tag_space + value unless value.nil?
    str += "\n"
    @out << str
  end

  def ul(hash)
    open_tag('ul') { li(hash) }
  end

  def li(hash)
    @level += 1
    hash.each do |key,value|
      open_tag('li',key) { ul(value) if value.is_a?(Hash) }
    end
    @level -= 1
  end

  def list
    ul(@hash)
    @out.join
  end
end

class HtmlListMaker < ListMaker
  def initialize(hash)
    super
    @tag_space = ""
  end

  def open_tag(tag,value=nil,&block)
    append("<#{tag}>",value)
    yield if block_given?
    append("</#{tag}>")
  end
end

class HamlListMaker < ListMaker
  def initialize(hash)
    super
    @tag_space = " "
  end

  def open_tag(tag,value=nil,&block)
    append("%#{tag}",value)
    yield if block_given?
  end

end

require 'yaml'

yaml = <<EOS
a:
 a1:
 a2:
b:
 b1:
  b11:
 b2:
EOS

hash = YAML.load(yaml) # {"a"=>{"a1"=>nil, "a2"=>nil}, "b"=>{"b1"=>{"b11"=>nil}, "b2"=>nil}}

puts HamlListMaker.new(hash).list

# %ul
#   %li a
#   %ul
#     %li a1
#     %li a2
#   %li b
#   %ul
#     %li b1
#     %ul
#       %li b11
#     %li b2

puts HtmlListMaker.new(hash).list

# <ul>
#   <li>a
#   <ul>
#     <li>a1
#     </li>
#     <li>a2
#     </li>
#   </ul>
#   </li>
#   <li>b
#   <ul>
#     <li>b1
#     <ul>
#       <li>b11
#       </li>
#     </ul>
#     </li>
#     <li>b2
#     </li>
#   </ul>
#   </li>
# </ul>