ruby-on-railsrubycontent-lengthxml-builder

Can I add file size to Builder XML output


I'm working on a ruby on rails app that generates a large XML document using a builder template, but I've hit a bit of a stumbling point.

The XML output must have a field that contains the file size in bytes. I think that I'll basically need to use the value that would populate the "Content-Length" header in the http response, but updating the value of the tag will obviously change the file size.

The output should looks something like this:

<?xml version="1.0" encoding="UTF-8"?>
<dataset>
  <metadata>
    <filesize>FILESIZE</filesize>
    <filename>FILENAME.xml</filename>
  </metadata>
    <data>
    .
    .
    .
    </data>
</dataset>

Is adding the file size in an XML tag possible using a builder template? If not, is there some method that I could use to achieve the required result?


Solution

  • Thanks to Garrett I was able to come up with the following (ugly) solution, it definitely needs improvement, but it does work:

    class XmlMetaInjector
      require 'nokogiri'
    
      def initialize(app)  
        @app = app  
      end  
    
      def call(env)  
        status, headers, response = @app.call(env)  
        if headers['Content-Type'].include? 'application/xml'
          content_length = headers['Content-Length'].to_i # find the original content length
    
          doc = Nokogiri::XML(response.body)
          doc.xpath('/xmlns:path/xmlns:to/xmlns:node', 'xmlns' => 'http://namespace.com/').each do |node|
             # ugly method to determine content_length; if this happens more than once we're in trouble
            content_length = content_length + (content_length.to_s.length - node.content.length)
            node.content = content_length
          end
    
          # update the header to reflect the new content length
          headers['Content-Length'] = content_length.to_s
    
          [status, headers, doc.to_xml]  
        else  
          [status, headers, response]  
        end 
      end # call(env)
    end