Pregunta

I'm using the following snippet to create some XML and it works absolutely fine.

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

The XML produced looks like this;

<?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>

Attempting to refine this with define_method means that the attributes are not "translated" (for want of a better word). Instead of using the type (e.g. 'murders') the XML now reads 'type'.

["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>

To summarize: in the original code I was able to iterate through a hash and use 'x.murder' to produce <murder>...</murder> but with define_method the same code produces <type> with attributes.

How is it possible to correctly define the element with define_method?

¿Fue útil?

Solución

You are actually calling x.type and o.type.singularize instead of calling x.murders and o.murder.
Since Nokogiri works by utilizing method_missing, I believe you should use x.send instead:

["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

When you want to call a method dynamically you cannot simply put the variable name as a method name, and hope it will be called:

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

Ruby's send API enables you to do that, by passing the method name as an argument:

Invokes the method identified by symbol, passing it any arguments specified. You can use __send__ if the name send clashes with an existing method in obj. When the method is identified by a string, the string is converted to a symbol.

a = 'to_i'
'123'.send(a) # => 123
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top