mongo_mapperのbelongs_toの参照が返すnilはnilじゃない

                       mongo_mapperを使い始める前に言っておくッ!
                    おれは今やつのスタンドをほんのちょっぴりだが体験した
                  い…いや…体験したというよりはまったく理解を超えていたのだが……
         ,. -‐'''''""¨¨¨ヽ
         (.___,,,... -ァァフ|          あ…ありのまま 今 起こった事を話すぜ!
          |i i|    }! }} //|        『belongs_toを設定したモデルで、参照するドキュメントが
         |l、{   j} /,,ィ//|        設定されていないインスタンスを保存したんだが、
        i|:!ヾ、_ノ/ u {:}//ヘ        その参照をifで評価したら真と判断された』
        |リ u' }  ,ノ _,!V,ハ |
       /´fト、_{ル{,ィ'eラ , タ人        な… 何を言ってるのか わからねーと思うが
     /'   ヾ|宀| {´,)⌒`/ |<ヽトiゝ        おれも何をされたのかわからなかった…
    ,゙  / )ヽ iLレ  u' | | ヾlトハ〉
     |/_/  ハ !ニ⊇ '/:}  V:::::ヽ        頭がどうにかなりそうだった…
    // 二二二7'T'' /u' __ /:::::::/`ヽ
   /'´r -―一ァ‐゙T´ '"´ /::::/-‐  \    keyやbelongs_toの記述ミスだとか
   / //   广¨´  /'   /:::::/´ ̄`ヽ ⌒ヽ    そんなチャチなもんじゃあ 断じてねえ
  ノ ' /  ノ:::::`ー-、___/::::://       ヽ  }
_/`丶 /:::::::::::::::::::::::::: ̄`ー-{:::...       イ  もっと恐ろしいものの片鱗を味わったぜ…

mongo_mapperのバージョンは 0.7.6 だ

$ gem list -d mongo_mapper

*** LOCAL GEMS ***

mongo_mapper (0.7.6)
    Author: John Nunemaker
    Homepage: http://github.com/jnunemaker/mongomapper
    Installed at: /Users/takeshi/.rvm/gems/ruby-1.8.7-p249@mongo_mapper

    A Ruby Object Mapper for Mongo
$ irb

irbで現象を確認する

require 'mongo_mapper'

**Notice: C extension not loaded. This is required for optimum MongoDB Ruby driver performance.
  You can install the extension as follows:
  gem install bson_ext

  If you continue to receive this message after installing, make sure that the
  bson_ext gem is in your load path and that the bson_ext and mongo gems are of the same version.

#=> true
MongoMapper.database = "test01"
#=> "test01"
MongoMapper.connection = Mongo::Connection.new("localhost", 27017)
#=> #<Mongo::Connection:0x10162bf40 @checked_out=[], @auths=[], @connection_mutex=#<Mutex:0x10162be28>, @logger=nil, @id_lock=#<Mutex:0x10162bea0>, @sockets=[], @timeout=5.0, @port=27017, @queue=#<ConditionVariable:0x10162bdd8>, @slave_ok=nil, @host="localhost", @nodes=[["localhost", 27017]], @safe_mutex=#<Mutex:0x10162be00>, @options={}, @size=1>


class Bar
   include MongoMapper::Document
   key :name, String
end
#=> #<MongoMapper::Plugins::Keys::Key:0x1015a1368 @default_value=nil, @type=String, @name="name", @options={}>

class Foo
  include MongoMapper::Document
  key :bar_id, ObjectId
  belongs_to :bar
end
#=> nil

Foo.create
#=> #<Foo _id: $oid4bfa44827693a1138e000001, bar_id: nil>


Foo.all
#=> [#<Foo _id: $oid4bfa44827693a1138e000001, bar_id: nil>]
foo1 = Foo.first
#=> #<Foo _id: $oid4bfa44827693a1138e000001, bar_id: nil>

b1 = foo1.bar
#=> nil

やってることは簡単だ。2つのドキュメントにマップされるクラスを作成して、FooがBarを参照できるように、Fooにはbar_idというキーを持ち、belongs_toを設定する。
そして、Fooのインスタンスに何も属性を指定せずにcreateしてドキュメントを保存し、その保存したものをfoo1として取得する。

この時のfoo1.barは何も指さない。nilを返すはずだ。そう表現されている。しかし。。。

b1 ? "T" : "F"
#=> "T"

b1が真として評価されているッ!

条件演算子が信じられない人のためにif...else...endで書いておこう

if b1
  puts "TRUE"
else
  puts "FALSE"
end
TRUE
#=> nil

やはりb1が真として評価されているッッッッッッ!


nilのはずなのに、b1はnilではないのか?

b1.nil?
#=> true

nil?に対してはtrueを返している・・・b1は何者なんだッッッ!?classを調べてやる

b1.class
#=> nil

おかしいぞッ!? nilのクラスはNilClassのはずッ!

nil.class
#=> NilClass

もしや黒魔術では・・・?b1の特異クラスは・・・?

class << b1; self; end
#=> #<Class:#<MongoMapper::Plugins::Associations::BelongsToProxy:0x10154b8a0>>

ビンゴ!!

b1.nil?
#=> true

という訳で、belongs_toで生成された参照先を取得するメソッドで、参照先が得られない場合の戻り値は、一見nilのように見えるがnilじゃない!!!
if、unless、条件演算子では期待通りに評価されないので、ARなどから移行する場合は気をつけろッ!!