scaffold全体の流れから分かったこと。

重複するところもあるかもしれないが、scaffoldが生成するコード全体の流れから理解できたことを書き留めておく。(表面的な動きを見て、経験則で理解したことなので、おそらくRailsの中の本質的な考え方は違っていると思うが...。)

処理はコントローラからビューの順で流れる

  • コントローラのメソッドを実行し、特にページ描画の指定がなければ、自動的にメソッド名と同じrhtmlファイルを利用して描画する。(例:listメソッドが呼び出されると、list.rhtmlを利用してページ描画する。)
  • renderやredirect_toでページ描画を実行していれば、メソッド名と同じrhtmlファイルでの自動描画はしない。
  • 対応するrhtmlファイルが存在しないと、白紙のページが表示される。
  • 1回の処理の中で、2回以上renderやredirect_toでページ描画を実行してしまうとエラーが発生する。

@で始まるインスタンス変数について

  • コントローラからビューへの情報の伝達には、「@」で始まるインスタンス変数*1を利用する。
  • コントローラの中で使った英字で始まる変数(ローカル変数)は、ビューの中では存在しないことになってしまう。
  • インスタンス変数の寿命は1回の処理の中だけである。ページの描画が終わって、次の処理*2が発生した時には、インスタンス変数に以前の内容は残っていない。

セッションとフラッシュ

  • セッションまたはフラッシュを利用すれば、前の処理の情報を、次の処理へ渡すことが出来る。(インスタンス変数では次の処理へ情報を渡すことが出来ない。)
  • セッションによる保存は、サーバに接続中ずっと保持される。(サーバに接続中の定義が分からない...。)
  • フラッシュによる保存は、次の処理まで保持される。(例:updateメソッドの中で、flash[:notice]に「更新が成功した」というメッセージを保存して、showメソッドに処理を転送(redirect_to)して、show.rhtmlとsoftwares.rhtmlによってページ描画)
  • 「更新が成功した」「エラーが発生した」などのメッセージを、次の処理で描画する時に便利な仕組みである。*3

以下はフラッシュのコードの流れ。

app/controllers/softwares_controller.rb:ここでフラッシュにメッセージを入れて、次の処理へ転送
  def update
    @software = Software.find(params[:id])
    if @software.update_attributes(params[:item])
      flash[:notice] = 'Software was successfully updated.'......更新が成功したら、flash[:notice] にメッセージを保存する。
      redirect_to :action => 'show', :id => @software......次の処理showメソッドに処理を転送する。
    else
      render :action => 'edit'
    end
  end
views/layouts/softwares.rhtml:フラッシュの表示


  Softwares: <%= controller.action_name %>
  <%= stylesheet_link_tag 'scaffold' %>


<p style="color: green"><%= flash[:notice] %></p>......flash[:notice]にメッセージがあれば一度だけ描画する。

<%= yield  %>




editからupdateへの情報の流れを再確認

理解を深めるために、@softwareを@itemに置き換えて見直してみた。

      • scaffoldではモデル名をインスタンス変数名とした@softwareが利用される。
      • ここでは、フォームのキーワードがインスタンス変数名に依存するのか、モデル名に依存するのか、確認のため@itemで置き換えて実験してみた。
http://0.0.0.0:3000/softwares/edit/6

上記URL入力を受け取ると...

softwares_controller.rb
  def edit
    @item = Software.find(params[:id])......[1]
  end

変数@itemに、データベースのsoftwaresテーブルのidが6の行への参照情報*4を代入する。

edit.rhtml
<h1>Editing software</h1>

<%= start_form_tag :action => 'update', :id => @item %>......[A]、[2]
  <%= render :partial => 'form' %>
  <%= submit_tag 'Edit' %>......[B]
<%= end_form_tag %>

<%= link_to 'Show', :action => 'show', :id => @item %> |
<%= link_to 'Back', :action => 'list' %>

[A]で、変数@itemの示すデータベースの情報を、_form.rhtmlの対応する項目に表示して、入力フォームを開始する。
[B]で作成されたEditボタンが押されると、updateメソッドを変数@itemの示すidで実行する。

_form.rhtml
<%= error_messages_for 'item' %>......[3]
<p><label for="item_title">Title</label><br/>......[4]
<%= text_field 'item', 'title' %></p>......[5]

<p><label for="item_description">Description</label><br/>
<%= text_area 'item', 'description'  %></p>

<p><label for="item_url">Url</label><br/>
<%= text_field 'item', 'url'  %></p>

[3]で、@itemの示すモデルSoftwareの看板errorsの内容を表示している。
[5]で、@itemのtitle項目の内容を表示して、入力フォームのテキストフィールドを作成する。

    • テキストフィールドを区別する名前として、item[title]を使用する。
    • ラベルのfor項目にitem_titleを設定することで、このテキストフィールドと関連付け出来る。

softwares_controller.rb
  def update
    @item = Software.find(params[:id])......[C]、[6]
    if @item.update_attributes(params[:item])......[D]、[7]
      flash[:notice] = 'Software was successfully updated.'
      redirect_to :action => 'show', :id => @software
    else
      render :action => 'edit'
    end
  end

updateメソッドは二つの情報を受け取って実行される。

    • 一つ目は、webブラウザのアドレス欄に表示されている情報。つまり、GETによる送信。http://0.0.0.0:3000/softwares/show/6
    • 二つ目は、フォームデータとして入力された情報。つまり、POSTによる送信。この情報は目に見えていない。

paramsを使うと、GET、POSTどちらで送信された情報であっても、同じ構文でキーワードを指定するだけで簡単に取り出すことが出来る。

    • params[:id]では、GETで送信された情報のid部分「6」を取り出している。
    • params[:item]では、POSTで送信された情報の、名前がitemの項目を取り出している。(入力フォームにはitem[title]、item[description]、item[url]と名前が付いているので、3つの情報が取り出されるようだ。)

[C]で、データベースのsoftwaresテーブルのid=6の行への参照情報*5が変数@itemに代入される。
[D]で、@itemの示すデータベース上の対象行を、入力フォームからparams[:item]で取り出した3つの情報で更新する。
つまり、以下7箇所のitemは関連しているようだ。

    • [1] @item = Software.find(params[:id])
    • [2] <%= start_form_tag :action => 'update', :id => @item %>
    • [3] <%= error_messages_for 'item' %>
    • [4] <label for="item_title">
    • [5] <%= text_field 'item', 'title' %>
    • [6] @item = Software.find(params[:id])
    • [7] if @item.update_attributes(params[:item])
      • validateを利用してみて、太字の@itemについても関連していることが判明したので修正した。
      • もし@itemを利用してデータベースとフォームの入力値を同期するなら、フォームのキーワードには'item'を利用する必要があるのだ。

*1:変数の頭に「@」を付けるだけで良い。

*2:URLアドレスの入力や、リンクのクリック、ブックマークの選択、入力フォームの送信、redirect_toによる処理の転送、などを行うこと

*3:webアプリケーションではよくある処理なのだろう。セッションでは、その都度、内容を消去してあげる必要がある。フラッシュなら表示した後、自動的に内容が破棄されるので使い勝手が良いはず。

*4:Macのファイル操作上のエイリアスや、Windowsでのショートカットのようなものと勝手に思い込んでいる

*5:Macのファイル操作上のエイリアスや、Windowsでのショートカットのようなものと勝手に思い込んでいる