Ruby言語やLinuxのネタが多いです。
November 27, 2006 [おもひで]
■ [Rails] Rails 1.2RC1対応
Rails 1.2RC1が出たのでRuby-GetText周り(CVS)の確認をしてみた。
大体は動いてるみたいなんだけど、テストが失敗するね。1.2リリースまでには直したいところだけど間に合うかな。すでにチャレンジされている方、パッチ歓迎です;)。
#DEPRECATIONのメッセージだけは直しました。
■ [Rails] ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy
Rails 1.2からは@cookies, @flash, @headers, @params, @request, @response, @sessionの各変数に直接アクセスするのがDeprecatedになっていて、そういったコードがあるとDEPRECATION WARNINGというメッセージを出すようになった。
それにしても、インスタンス変数にアクセスするだけでWarningを出す、なんて、どういう風に実装しているんだろう。インスタンス変数へのアクセス時にフックなんてできたっけな?と思ってちょっと調べてみたので、メモ。
本当の実装は、
activesupport-1.3.1.5618/lib/active_support/deprecation.rb
actionpack-1.12.5.5618/lib/action_controller/base.rb
activesupport-1.3.1.5618/lib/active_support/core_ext/module/attr_internal.rb
を見てほしいんだけど、それを超簡略化すると以下のような感じ。
# activesupport-1.3.1.5618/lib/active_support/deprecation.rbから抜粋
class DeprecatedInstanceVariableProxy
instance_methods.each { |m| undef_method m unless m =~ /^__/ }
def initialize(instance, method, var = "@#{method}")
@instance, @method, @var = instance, method, var
end
private
def method_missing(called, *args, &block)
warn caller, called, args
target.__send__(called, *args, &block)
end
def target
@instance.__send__(@method)
end
def warn(callstack, called, args)
puts ("#{@var} is deprecated! Call #{@method}.#{called} instead of #{@var}.#{called}. Args: #{args.inspect}")
end
end
# 呼出側:actionpack-1.12.5.5618/lib/action_controller/base.rbから抜粋
class Test
def initialize
@_headers = []
@headers = DeprecatedInstanceVariableProxy.new(self, "headers")
end
def headers
#実際は、attr_internalでセットしている。以下を参照
#activesupport-1.3.1.5618/lib/active_support/core_ext/module/attr_internal.rb
@_headers
end
end
# わかりやすいようにサブクラスで実験
class Test2 < Test
def initialize
super
@headers[0] = 1 #ここ
end
end
p Test2.new
実行結果: @headers is deprecated! Call headers.[]= instead of @headers.[]=. Args: [0, 1] @headers is deprecated! Call headers.inspect instead of @headers.inspect. Args: [] #<Test2:0x2aaaaab0a700 @headers=[1], @_headers=[1]>
ポイントは、DEPRECATEDなインスタンス変数にアクセスするというのはつまり、DEPRECATEDなインスタンス変数の*インスタンスメソッドに*アクセスする、ということ。
簡単に説明すると、まず、@headersの代わりに@_headersを用意。headersメソッドはこちらを使うようになる(attr_internalで設定)。
@headersは今までのインスタンス(ここでは配列)をそのまま使うのでは無く、DeprecatedInstanceVariableProxyを噛ませて、そのインスタンスメソッドにアクセスさせるようにする。
DeprecatedInstanceVariableProxyの中身はどうなっているかというと、インスタンスメソッドは全てundef。インスタンスメソッドにアクセス(ここでは@headers[0] = 1)されてきたらmethod_missing経由でWarningを出力し、元の(self.headers)メソッドを呼び出すようにする。
#なお、実行結果が2行出てしまっているのはご愛嬌(inspectのWarningのやつね)
なるほど、かしこいのう。というよりも、こういう(あると便利なんだけど、遅くなりそうで、かつ、あまりアプリ・ライブラリとしての本筋には関係のない)実装をスクリプト言語で躊躇無くしてしまうのがいろんな意味ですごい。
#もともとRailsは遅くなりそうな実装テンコモリだけどねー。
