社内で質問されたので回答を書いておきます。
以下のようなモデルがあったとき、attachable_typeに存在しないモデル名(たとえば"Hoge")が入っていたらどのような挙動をするのか?
class AttachedImage < ActiveRecord::Base belongs_to :attachable, :polymorphic => true end
やってみればわかりますが、せっかくなのでソースを読んでみましょう。
当然、手元にrailsのソースコードはcloneしてありますよね?
git clone git://github.com/rails/rails.git cd rails git checkout v3.2.8
結論から言うと、該当のソースコードは、activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb
です。
belongs_toの定義から追いかけてみましょう。
このクラスはBelongsToPolymorphicAssociation → BelongsToAssociation → SingularAssociationと継承関係があります。
まず、belongs_toの定義はactive_record/associations.rb:1427です。
# active_record/associations.rb def belongs_to(name, options = {}) Builder::BelongsTo.build(self, name, options) end
ActiveRecord::Associations::Builder::BelongsToのbuild(未定義なので先祖であるActiveRecord::Associations::Builder::Associationのself.build)が呼ばれます。
その後buildで内部で、define_accessorsを呼んでいますね。
# active_record/associations/builder/association.rb def self.build(model, name, options) new(model, name, options).build end def build validate_options reflection = model.create_reflection(self.class.macro, name, options, model) define_accessors reflection end def define_accessors define_readers define_writers end def define_readers name = self.name mixin.redefine_method(name) do |*params| association(name).reader(*params) end end def define_writers name = self.name mixin.redefine_method("#{name}=") do |value| association(name).writer(value) end end
belongs_toで定義したassociation名の自動メソッド(この例だとattachable)の定義が見つかりました。
つまり、association(name).reader(*params)が呼ばれるわけですね。association(name)はActiveRecord::Associationsで定義されていて、この場合だとBelongsToPolymorphicAssociationが生成されます。
# active_record/associations/singular_association.rb def reader(force_reload = false) if force_reload klass.uncached { reload } elsif !loaded? || stale_target? reload end target end
ちなみにこのtargetはsuperのattr_readerで定義されたもので、実体は@targetのアクセサです。
reloadの中でload_targetが呼ばれ、その中でfind_target?がtrueならfind_targetの結果が@targetに代入されます。
find_target?の定義を見てみましょう。
# active_record/associations/belongs_to_association.rb def find_target? !loaded? && foreign_key_present? && klass end
klassが呼ばれていますね。klassはBelongsToPolymorphicAssociationで再定義されていて、以下のようになっています。
# active_record/associations/belongs_to_polymorphic_association.rb def klass type = owner[reflection.foreign_type] type.presence && type.constantize end
ようやくたどり着きました。
ここのtype.constantize部分で、NameError: uninitialized constant Hogeというエラーが出ます。
※find_target?の定義の通り、attachable_idがblank(nilや空)の場合には、klassが呼ばれないので、そのままnilになります。
※ちなみに、エラーにならない場合、このままSingularAssociationのfind_targetに進み、AssociationScopeのscope→add_constratinsでid制限がかかったscopeが生成された後、そのscopeのfirstが取得されます。
あー長かった。
結論:NameErrorが発生します。