ActiveResourceが使うXMLパーサの切り替え
やりたいこと
ActiveResourceが使うXMLパーサを切り替えたい。JSONを使えれば、そっちの方が高速に処理できそうですが、メッセージフォーマットがXMLにしか対応していないウェブサービスもありますよね。
検索してみると、関係しそうなところで、次のようなサイトが見つかりました。
ソースのトレース
自分でも、コードを追って確認してみたいと思います。railsのバージョンは2.3.2。まずは、findから。
ActiveResource::Base.find
C:\ruby\lib\ruby\gems\1.8\gems\activeresource-2.3.2\lib\active_resource\base.rb
def find(*arguments) scope = arguments.slice!(0) options = arguments.slice!(0) || {} case scope when :all then find_every(options) when :first then find_every(options).first when :last then find_every(options).last when :one then find_one(options) else find_single(scope, options) end end
ActiveResource::Base.findの第1引数を:oneにしているので、find_oneが呼び出されます。
ActiveResource::Base.find_one
C:\ruby\lib\ruby\gems\1.8\gems\activeresource-2.3.2\lib\active_resource\base.rb
# Find a single resource from a one-off URL def find_one(options) case from = options[:from] when Symbol instantiate_record(get(from, options[:params])) when String path = "#{from}#{query_string(options[:params])}" instantiate_record(connection.get(path, headers)) end end
:fromとして文字列を指定しているので、when String以下が実行されます。connection.getでHTTP GETをしているようです。
ActiveResource::Connection.get
C:\ruby\lib\ruby\gems\1.8\gems\activeresource-2.3.2\lib\active_resource\connection.rb
# Execute a GET request. # Used to get (find) resources. def get(path, headers = {}) format.decode(request(:get, path, build_request_headers(headers, :get)).body) end
format.decodeでデコードしているようですが、まずは、formatが何ものかを確認します。base.rbファイル内で定義されています。
ActiveResource::Base.format
C:\ruby\lib\ruby\gems\1.8\gems\activeresource-2.3.2\lib\active_resource\base.rb
# Returns the current format, default is ActiveResource::Formats::XmlFormat. def format read_inheritable_attribute(:format) || ActiveResource::Formats[:xml] end
:formatが設定されていないと、デフォルトでXMLとなります。ActiveResource::Formats[]を見ます。
ActiveResource::Formats[]
C:\ruby\lib\ruby\gems\1.8\gems\activeresource-2.3.2\lib\active_resource\formats.rb
# Lookup the format class from a mime type reference symbol. Example: # # ActiveResource::Formats[:xml] # => ActiveResource::Formats::XmlFormat # ActiveResource::Formats[:json] # => ActiveResource::Formats::JsonFormat def self.[](mime_type_reference) ActiveResource::Formats.const_get(mime_type_reference.to_s.camelize + "Format") end
なので、先ほどのformat.decodeのformatは、ActiveResource::Formats::XmlFormatインスタンスとなります。format.decodeを見ます。
ActiveResource::Formats::XmlFormat.decode
def decode(xml) from_xml_data(Hash.from_xml(xml)) end
Hash.from_xml
C:\ruby\lib\ruby\gems\1.8\gems\activesupport-2.3.2\lib\active_support\core_ext\hash\conversions.rb
def from_xml(xml) typecast_xml_value(unrename_keys(XmlMini.parse(xml))) end
XmlMini.parse(xml)でパースしています。
XmlMini.parse
(ただし、パーサとしてREXMLが指定されている場合です。)
C:\ruby\lib\ruby\gems\1.8\gems\activesupport-2.3.2\lib\active_support\xml_mini\rexml.rb
def parse(string) require 'rexml/document' unless defined?(REXML::Document) doc = REXML::Document.new(string) merge_element!({}, doc.root) end
rexml.rbの1つ上のフォルダにあるxml_mini.rbを見てみます。
XmlMini
C:\ruby\lib\ruby\gems\1.8\gems\activesupport-2.3.2\lib\active_support\xml_mini.rb
module ActiveSupport # = XmlMini # # To use the much faster libxml parser: # gem 'libxml-ruby', '=0.9.7' # XmlMini.backend = 'LibXML' module XmlMini extend self attr_reader :backend delegate :parse, :to => :backend def backend=(name) if name.is_a?(Module) @backend = name else require "active_support/xml_mini/#{name.to_s.downcase}.rb" @backend = ActiveSupport.const_get("XmlMini_#{name}") end end def with_backend(name) old_backend, self.backend = backend, name yield ensure self.backend = old_backend end end XmlMini.backend = 'REXML' end
コメントアウトされている説明を読むと、より高速なlibxmlを使う場合には、バージョン0.9.7の'libxml-ruby'gemをインストールし、XmlMini.backend = 'LibXML'とせよ、とのことです。デフォルトは、XmlMini.backend = 'REXML'ですね。
ということで
指示通りに試してみようと思います。