:yの悲劇
GPS情報を格納するのに、
class Entry < ActiveRecord::Base
has_one :location
end
class Location < ActiveRecord::Base
# 実際にはGeometry型で格納するので、アクセサを定義
def x
point.x
end
def y
point.y
end
endこんなモデルを作ると・・・
$ script/console
>> entry = Entry.find(:first)
>> entry.location.x
=> 135.0
>> entry.location.y
=> 35.0
>> entry.location.send :x
=> 135.0
>> entry.location.send :y
ArgumentError: wrong number of arguments (0 for 1)
from (irb):35:in `y'
from (irb):35:in `send'
from (irb):35xは大丈夫なのにyはだめ?
yの正体は、pのようにyamlを吐くprivate method、Kernel#y in yaml.rb。 何故だ?
entry.location の中身は、実はAssociationProxyで、ActiveRecordのソースを見るとこんな風になっている。
# lib/active_record/associations/association_proxy.rb
module ActiveRecord
module Associations
class AssociationProxy #:nodoc:
attr_reader :reflection
alias_method :proxy_respond_to?, :respond_to?
alias_method :proxy_extend, :extend
instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?|^proxy_respond_to\?|^proxy_extend|^send)/ }entry.location.send :x すると、AssocationProxy#xが無いのでtargetにsendされるが、entry.location.send :y だと、Kernel#yが見つかってしまうのか・・・
当然entry.location.target.send :y だとうまくいく。
Ruby1.9 だとsendの仕様が違ってうまくいきそう。
4 comments »
-
By keisuken 21/12/2006 at 21h58
-
By nanki 22/12/2006 at 13h20
名前を変更するのが一番早い解決法だったんですが・・・
せっかくなので原因を探ってみました。 メタ情報を得るメソッド類もほとんどundefされているので、ソースを見るまで意味不明で。
位置情報を扱うと、コアの部分は似たような構成になりますね。
-
By babie 11/01/2007 at 00h09
人柱ありがとう!w latitude, longitude にしまつ。
-
By nanki 11/01/2007 at 00h47
AssociationProxy において、private なメソッドにアクセスできてしまうsend を使ってるのがおかしな話なんですが。
パッチ書く気力が起きない・・・
微妙に今やってるのと重なる(もしかして同じ構成のシステム(^;). とりあえずこちらはlatitude,longitudeにしてます. #Google Mapsが分秒単位じゃなくてはまった口 Ruby勉強ネタになるのかな.