XML-класс для частичного файла, используемого в XML-классе Nokogiri

Использование рельсов 3.0.7 и рубина 1.9.2.

Я использую Nokogiri для создания собственного XML. Я вызываю файл частичного построителя в файле основного вида. Но класс, используемый частично для xml, не является Nokogiri. Кто-нибудь может объяснить, почему класс xml не Nokogiri?

index.xml.erb

<%= Nokogiri::XML::Builder.new(:encoding => 'UTF-8') { |xml|
  puts "xml class in main file #{xml.class}"
  xml.users {
    @users.each do |user|
      xml << render(:partial=>"users/user", :locals=>{:user=>user, :xml => xml})
    end
  }
}.to_xml.html_safe
%>

_user.builder

puts "xml class in builder file #{xml.class}"
xml.user {
  xml.id user.id
  xml.name user.name
  xml.email user.email
}

В application.rb, используя:

ActiveSupport::XmlMini.backend = 'Nokogiri'

Вывод :

xml class in main file Nokogiri::XML::Builder
xml class in builder file Builder::XmlMarkup

person Navazish    schedule 21.05.2013    source источник


Ответы (1)


IIUC ActiveSupport::XmlMini.backend = 'Nokogiri' предназначен только для анализа параметров XML с помощью Nokogiri вместо ReXML, а не для создания шаблонов компоновщика.

Вы создаете Nokogiri::XML::Builder вручную, а затем полагаетесь на render для создания партиала, поэтому ваши выходные данные согласуются.

Используйте следующее (в config/initializers/nokogiri_builders.rb для фактического использования Nokogiri через рендеринг (плюс вы можете отказаться от использования файлов .xml.erb для создания собственного экземпляра Nokogiri::XML::Builder и просто использовать .builder тоже):

# config/initializers/nokogiri_builders.rb
# Using Nokogiri::XML::Builder in lieu of stock pure ruby Builder for Rails builder templates

module Nokogiri
  module XML
    class Builder
      # handle instruct! instead of emitting nonsense tag
      def instruct!(tag, options = {})
        case tag
        when :xml
          @version = options[:version]
          @encoding = options[:encoding]
        else
          raise NotImplementedError(tag.inspect)
        end
      end

      # redefined, to recover what instruct! gave us
      def to_xml(*args)
        if Nokogiri.jruby?
          options = args.first.is_a?(Hash) ? args.shift : {}
          unless options[:save_with]
            options[:save_with] = Node::SaveOptions::AS_BUILDER
          end
          args.insert(0, options)
        end

        if @version || @encoding
          # set options according to what was given previously
          args[0][:version] = @version if @version
          args[0][:encoding] = @encoding if @encoding
          # do emit a declaration
          args[0].delete(:save_with)
        end

        @doc.to_xml(*args)
      end
    end
  end
end

module ActionView
  module Template::Handlers
    class Builder
      # Default format used by Builder.
      class_attribute :default_format
      self.default_format = :xml

      def call(template)
        require_engine
        code = <<-CODE
          builder = Nokogiri::XML::Builder.new do |xml|
            #{template.source}
          end
          # default to not emit a declaration (use instruct! instead)
          save_options = Nokogiri::XML::Node::SaveOptions::NO_DECLARATION
          # default to UTF-8, as it's Rails default encoding
          options = { encoding: 'UTF-8', indent: 2, save_with: save_options }
          self.output_buffer = builder.to_xml(options)
        CODE

        code
      end

      protected

      def require_engine
        @required ||= begin
          require 'nokogiri'
          true
        end
      end
    end
  end
end

Однако будьте осторожны, Nokogiri строит полный DOM перед генерацией вывода с помощью to_xml, в то время как Builder объединяет строки на лету. Это может быть или не быть тем, что вам нужно, особенно для больших наборов данных. Однако в обоих случаях полная окончательная строка находится в памяти.

person Lloeki    schedule 27.09.2013