如何使用define_method正确定义XML元素?

时间:2014-03-31 15:28:35

标签: ruby metaprogramming nokogiri

我正在使用以下代码段来创建一些XML,它的工作原理非常好。

def outputs_xml(dir, t_items, x)
  x.murders {
    t_items.values.each do |i|
      x.murder {
        attributes_xml(dir, i, x)
      }
    end
  }
end

生成的XML看起来像这样;

<?xml version="1.0"?>
<outputs>
  <murders>
    <murder>
      <classification>Macabre</classification>
      <title>An Old Macabre Murder</title>
      <path>C:\an_old_macabre_murder.pdf</path>
    </murder>
  </murders>
</outputs>

尝试使用define_method对其进行优化意味着属性未被“翻译”(因为缺少更好的单词)。 XML不再使用类型(例如“谋杀”),而是读取“类型”。

["murders", "mysteries", "thrillers"].each do |type|
  define_method("#{type}_xml") do |dir, items, o|
    x.type {
      items.values.each do |i|
        o.type.singularize{
          o.classification i[1].to_s
          o.titles i[2]
          o.path "#{dir}/#{i[3]}.pdf"
        }
      end
    }
  end

...

<?xml version="1.0"?>
<outputs>
  <type>
    <type class="singularize">
      <classification>Macabre</classification>
      <title>An Old Macabre Murder</title>
      <path>C:\an_old_macabre_murder.pdf</path>
    </type>
  </type>
</outputs>

总结一下:在原始代码中,我能够遍历哈希并使用'x.murder'来生成<murder>...</murder>但是使用define_method相同的代码会生成带有属性的<type>

如何使用define_method

正确定义元素

1 个答案:

答案 0 :(得分:2)

您实际上是在致电x.typeo.type.singularize而不是致电x.murderso.murder。 由于Nokogiri使用method_missing工作,我相信您应该使用x.send代替:

["murders", "mysteries", "thrillers"].each do |type|
define_method("#{type}_xml") do |dir, items, o|
  x.send(type) {
    items.values.each do |i|
      o.send(type.singularize) {
        o.classification i[1].to_s
        o.titles i[2]
        o.path "#{dir}/#{i[3]}.pdf"
      }
    end
  }
end

如果要动态调用方法 ,则不能简单地将变量名称作为方法名称,并希望它将被调用:

a = 'to_i'
'123'.a # <= ERROR - _not_ '123'.to_i

Ruby的send API使您可以通过将方法名称作为参数传递来实现:

  

调用 symbol 标识的方法,并传递任何参数   指定。如果名称发送冲突,您可以使用__send__   obj中的现有方法当方法由字符串标识时,   string被转换为符号。

a = 'to_i'
'123'.send(a) # => 123