長くjavaeeの人だったんですが最近RubyOnRailsの学習を始めました(周回遅れもいい所です)
scaffoldで生成されたソースを追っている所から始めているのですが、そこで詰まった所をQiitaへのテスト投稿も兼ねて書いておきます。
scaffoldを利用する新規登録(new)と更新(edit)で別々のhtmlテンプレートが作成されますが、内部ではformの入力項目を共通化した_form.html.erbをそれぞれ呼び出しています。
結果、同じ入力フォームを備えた画面が作成されるのですが、submitボタンの文面と呼び出されるURLが違っていました。
_form.html.erbの記述はかなりシンプルで、ここどうやって切り替えてるんだろ?と思いましたが、form_for関数によって書き出されるHTMLを切り替えてるんですね。
<%= form_for(@book) do |f| %>
これによって
<form ... action='books' class="new_book" id="new_book" method="post">
と書き出されたり
<form ... action='books/1' class="edit_book" id="edit_book_1" method="post">
になったり。
さて、そこで自分が参考にしている本には、
切り替えはform_forに渡されるオブジェクトの内容が空であるかどうかによって(正確にはオブジェクトの内容が新規レコードであるかどうかによって)
と書いてあります。
また、別のWEB上の解説記事にはidが入っていたらとありました。
なんか、アバウトな...
オブジェクトが新規レコードであるかとは何を以てでしょう?
さすがに無いでしょうが、id項目に値が入っていたらとも思われかねません。
(確かに自動更新される項目で有る事は前段階で解説されてましたけど)
こんな時はどんな言語も変わらず、捜査の基本、ソースを当たれですね。
action, method = object.respond_to?(:persisted?) && object.persisted? ? [:edit, :patch] : [:new, :post]
これなら分かる。
persisted?メソッドで、永続化されている状態であるかを判定しているんですね。
persisted?がfalse返す条件は大体推測はつきますが、一応確かめ。
book = Book.new
p "1:"+book.persisted?.to_s#まだ保存されてないから false
book.id = 100
p "2:"+book.persisted?.to_s #インスタンスのフィールドに値が入る事でないはず。これもfalse
book.save(title:"test")
p "3:"+book.persisted?.to_s #保存されたのでtrue
book2 = Book.find(book.id)
p "4:"+book2.persisted?.to_s #DBから取ってきたのでtrue
book2.destroy
p "5:"+book2.persisted?.to_s #DBから消されたはずなのでfalse
実行すると
"1:false"
"2:false"
"3:true"
"4:true"
"5:false"
予想通りですね。
(ちなみにすぐcommit、insert後commitしていない状態ではどうなるかは次回以降で)
ということで、railsにおける新規レコードとはActiveRecordクラスのインスタンスで、Newされた後、DBに保存される前の状態って事で納得です。
余談なのですが、view領域のメソッドでモデルがDBに保存されているか否かを判定している事に若干違和感を感じました。
MVCの考え方では、viewはモデルを見て振る舞いを変えるとされているので判定して表示を変える事はOK。
しかし、厳密にはモデル=DBレコードでは無いはずです。
モデルがDB保存有無情報を持つのが前提である所がちょっと引っかかったんです。
ただ、これは簡素なWebアプリケーションではモデルのデータ構造はDBテーブルの構成と同一であると
見なし、モデルの理解を助けMVC構造で作りやすくするRailsフレームワークの工夫だと感じました。
「DBレコードデータはビジネスロジックを持たないO/Rマッパー経由のオブジェクトです、それらを取得しviewが参照するデータ構造とビジネスロジックを持ったクラス=モデルと作りましょう!」より、
最初の最初はテーブル構造がモデルのデータ構造であり、CRUD操作の過程がビジネスロジックになるクラス(ActiveRecord)をモデルであると説明するのが簡単だし作る方もシンプルにできる様に思います。
そのある種割り切った考え方でview/controllerの部分も便利な機能があり、Railsフレームワークが広く受け入れられた要因の一つなのかなー。
といったところで、ActiveRecordの思想が実感できました。
複数テーブル、必ずしもDBに保存する必要がないもの等々、複雑なモデルが必要になったらActiveModelといった発展系もあるし。
しかし、複数テーブルで構成されるモデルを作る時が今後あるなら、models直下にそれらテーブルのActiveRecordクラスを置くのに抵抗感が出てきそう。
これはモデルのデータ構造作るのに必要な情報を持っているだけのテーブルであって、決してモデルではないんだ、って。
ま、まだscaffoldから先に進んでないし、学習を進めて色々なケースに触れる段階ですね。