背景
最近、コードリーディングが話題になりました。
ソースコードが読めることは武器になるようです。というわけでソースコードを読みます。
お題
MySQLのカラムの型によって、FactoryBot.build
でSQLが発行されるときとされないときがある現象に出会いました。
この挙動がFactoryBotのものかActiveRecordの物か切り分けます。 いきなりActiveRecordのソースコードに突入するのは大変なので、 より小さい方のFactoryBotのソースコードを読みました。
FactoryBot内に振る舞えを変えるソースコードがあればよし。 なくてもActiveRecordのどのメソッドを呼んでいるかわかれば、ActiveRecordのソースコードを読むときの入り口が決定できます。
結論
今回、読みたいのは、FactoryBot.build
を実行している箇所です。
def object @evaluator.instance = build_class_instance build_class_instance.tap do |instance| attributes_to_set_on_instance.each do |attribute| instance.public_send("#{attribute}=", get(attribute)) @attribute_names_assigned << attribute end end end
感想
FactoryBotは、古き良きデザインパターンが駆使されていました。 次のような構成です。
FactoryBot.build
とFactoryBot.create
を表すStrategyインターフェース- Strategyを決定するStrategyCalculator
- それを使ってStrategyとEvaluationを組み合わせて生成するFacotry*1
まあ、ここまでみてもAttributeAssigner出てこないんですよ?エグくないですか?
AttributeAssignerはEvaluationから呼び出されます。
EvaluationはFactoryBot.create
の実行を、AttributeAssignerはFactoryBot.build
の実行を担っています。
「は?」っておもいませんか?「Strategyでわけたはずじゃ?」って。
Strategyでわかれているのは、次の部分です。
- createはbuildしてからcreateする
- buildはbuildだけする
buildとcreateの実体はEvaluationとAttributeAssignerにあります。
createの実体はさらにエグいです。FactoryBotではcreateで実行するメソッドをto_create
というメソッドで変更できます。変更しない場合のデフォルトの設定はConfigurationクラスに定義されています。
to_create(&:save!)
はい。save!
です。
Rubyでここまで古典的なデザインパターンをきっちり守って書かれているソースコードも珍しいように思います。
*1:FactoryBotの中核がFactoryクラスなの、なかなか洒落が効いてますね。このためだけにFacotryパターンを採用したいくらいです