ActiveSupportのXMLパーサの実装をLibXMLに変更できない件
ActiveResourceが使うXMLパーサの切り替え、Windowsにlibxml-rubyインストールの続きです。Railsアプリのconfig\environment.rbに設定情報を記述することで、ActiveSupportのXMLパーサの実装をLibXMLに切り替えられないかと考えています。(先日の日記では、ActiveResourceのXMLパーサとしていましたが、ActiveResourceはActiveSupportのXMLパーサを参照しているようです。)
いろいろ試してみましたが、だめでした。ご存知の方、いないでしょうか。
とりあえず、やったことをメモします。
environment.rbの設定
environment.rbをこんな感じにすればできるかなと思いました。でも、サーバを起動すると、エラー終了してしまいます。
Rails::Initializer.run do |config| #中略 config.active_support["XmlMini.backend"] = 'LibXML' end
そのときに、コマンドプロンプトに出力されたメッセージです。メソッドがないとのこと。
c:/ruby/lib/ruby/gems/1.8/gems/rails-2.3.2/lib/initializer.rb:584:in `send': und efined method `XmlMini.backend=' for ActiveSupport:Module (NoMethodError)
トレース
ソースをトレースしてみます。トレースしてみて、上に書いたような感じでうまくいくんじゃないかと思った次第です。
まず、environment.rbでは、Rails::Initializer.runを呼び出しています。(1)、(2)、(3)の順に説明していきます。
C:\ruby\lib\ruby\gems\1.8\gems\rails-2.3.2\lib\initializer.rb
def self.run(command = :process, configuration = Configuration.new) # (1) yield configuration if block_given? # (2) initializer = new configuration initializer.send(command) # (3) initializer end
(1) configuration = Configuration.new
Rails::Initializer.runは、引数なしで呼び出されているので、デフォルトで、Configurationをnewしています。
initializer.rbの下の方へ行くと、Configurationのコンストラクタが定義されています。Rails::OrderedOptionsを生成し、active_supportに代入しています。
# Create a new Configuration instance, initialized with the default # values. def initialize 中略 self.active_support = Rails::OrderedOptions.new end
initializer.rbのさらに下の方へ行くと、Rails::OrderedOptionsクラスが定義されています。これは、key-valueストアですね。
# Needs to be duplicated from Active Support since its needed before Active # Support is available. Here both Options and Hash are namespaced to prevent # conflicts with other implementations AND with the classes residing in Active Support. class Rails::OrderedOptions < Array #:nodoc: def []=(key, value) key = key.to_sym if pair = find_pair(key) pair.pop pair << value else self << [key, value] end end def [](key) pair = find_pair(key.to_sym) pair ? pair.last : nil end def method_missing(name, *args) if name.to_s =~ /(.*)=$/ self[$1.to_sym] = args.first else self[name] end end private def find_pair(key) self.each { |i| return i if i.first == key } return false end end
ここまでで、config.active_support変数は、空の配列(keyとvalueのペアを要素とする)として初期化されることがわかりました。
(2) yield configuration if block_given?
ここで、environment.rbの"Rails::Initializer.run do |config| end"ブロックが実行されます。ブロック内に記述した、
config.active_support["XmlMini.backend"] = 'LibXML'
が呼び出されます。Rails::OrderedOptionsの[]=(key, value)が呼び出され、("XmlMini.backend", 'LibXML')が(key,value)として登録されます。
(3) initializer.send(command)
commandのデフォルトは:processなので、processメソッドが呼び出されます。processメソッドで、initialize_framework_settingsが呼び出されています。
# Sequentially step through all of the available initialization routines, # in order (view execution order in source). def process 中略 initialize_framework_settings 中略 end
initialize_framework_settingsでは、ActiveSupportの"#{setting}="メソッドを呼び出します。ここで、settingとは、先ほど説明したkey-valueのkeyに対応します。
C:\ruby\lib\ruby\gems\1.8\gems\rails-2.3.2\lib\initializer.rb
# Initializes framework-specific settings for each of the loaded frameworks # (Configuration#frameworks). The available settings map to the accessors # on each of the corresponding Base classes. def initialize_framework_settings configuration.frameworks.each do |framework| base_class = framework.to_s.camelize.constantize.const_get("Base") configuration.send(framework).each do |setting, value| base_class.send("#{setting}=", value) end end configuration.active_support.each do |setting, value| ActiveSupport.send("#{setting}=", value) end end
これを利用して、ActiveSupportモジュール内で、XmlMini.backend = 'LibXML'を呼び出そうと目論んでいました。
トレースしてみて、
keyとしてメソッド名だけじゃなく、モジュール名"XmlMini"まで入れていることが問題かなとも思います。単にrubyを知らないだけかも。。