rails読解シリーズ第5回目はsaveメソッド。
# File activerecord/lib/active_record/base.rb, line 2575
def save
create_or_update
end
create_or_updateメソッドが呼ばれているだけのようです。
# File activerecord/lib/active_record/base.rb, line 2916
def create_or_update
raise ReadOnlyRecord if readonly?
result = new_record? ? create : update
result != false
end
2行目で、対象のインスタンスが読み取り専用かどうか確認するようです。
メソッドの名前からして、readonly属性はboolean型で定義して、readonly: trueとすることで、利用することができそうです。
プログラマーが自ら定義することもできますが、railsのことだし、メソッドによって定義する方法もありそう。
# File activerecord/lib/active_record/base.rb, line 628
def readonly?
@readonly
end
読み取り専用のインスタンスであった場合はReadOnlyRecordエラーが出力されます
class ReadOnlyRecord < ActiveRecordError
end
result = new_record? ? create : update
3項演算子の条件はnew_record?メソッド。
名前の通り、新規レコードか確認するものでしょう。
# File activerecord/lib/active_record/base.rb, line 2554
def new_record?
@new_record || false
end
なんとなく予想できたかもしれませんが、こちらも先程のreadonly?メソッドと同じ仕様のようです。
new_record属性にtrueを設定していた場合はtrueを返し、特に指定がなければfalseとなる模様。
先程は|| falseの記述がなかったので、どう違う動きをするのか気になります。
rubyの場合、値が設定されていない=nilはfalseと判定されます。
わざわざ設定する理由としては2つ考えられます。
- true、false以外の値が代入された場合にtrue判定してしまうのを防ぐため
- new_record属性として参照したメモリの位置に以前使用したときのデータが残っている場合の誤動作を防ぐため
2.については組み込みで用いられるC言語など高級でない言語が用いられる分野で考慮されることがあるそうです。
Rubyのような言語でも懸念は必要なのでしょうか。
逆に、readonly?メソッドに同じ処理が施されていないということは、readonly属性として、必ず値が設定される仕組みがあるということでしょうか。
result = new_record? ? create : update
3項演算子に戻ります。
新しいレコードであればcreateメソッド、そうでなければupdateメソッドが呼び出されます。
ここはシンプルですね。
result != false
3行目でresultに値が代入された場合はこの等式はtrueとなり、それ以外の場合はfalseとなります。
よくコントローラで
if save
redirect_to xxx
else
render :new
end
のような用いられ方をしますが、4行目の等式の結果が返されて、それをif文に使用しているということですね。
saveメソッドの読解は完了です。
ついでに類似メソッドとして、save!メソッドを読んでみましょう。
save!メソッドの読解
# File activerecord/lib/active_record/base.rb, line 2592
def save!
create_or_update || raise(RecordNotSaved)
end
create_or_updateはsaveメソッドと同じ動きですね。
save!メソッドの特徴は保存に失敗したときにエラーを吐くこと。
保存に失敗するとcreate_or_updateがfalseを返します。
A||BのときAがfalseの場合はBが判定されます。
ここでraiseメソッドによりRecordNotSavedエラーが出力されます。
class RecordNotSaved < ActiveRecordError
attr_reader :record
def initialize(message = nil, record = nil)
@record = record
super(message)
end
end
このクラスのモデルにより、エラーメッセージが出力されるようです。
エラーの動作の仕組みは勉強にはなりそうですが、今回の記事の趣旨からは外れるので、別の機会に筆は譲りましょう。
まとめ
saveメソッドの動き
- 読み取り専用か確認し、必要に応じてエラーを吐く
- 新規データかどうかによってcreateまたはupdateメソッドを呼び出す。
- 結果に応じてtrueまたはfalseを返す
save!メソッドの動き
saveメソッドの中で上記1~3を実行するメソッドが3にてfalseを返した時、RecordNotSavedエラーを吐く
感想
- railsの学習を始めた頃のイメージはcreateはnew+saveだよ〜とおぼえていたので、saveの中でcreateメソッドが呼ばれていたのは驚きだった
- create/updateメソッドとエラー周りの仕組みに関心をもったので読んでみたい。
- めちゃくちゃ重要な機能をシンプルで短いコードで実現していて美しい!! 特に4行目。
今回もマニアックな記事を読んでいただきありがとうございました。
またお会いしましょう。