【Rails:2】コントローラとビュー

HTTPの基礎

  • HTTP::ブラウザとサーバが情報を送受信するための通信手段
    • リクエスト(ブラウザがサーバに送る要求)
GET /ctr_name/index HTTP/1.1
(以下、ブラウザとサーバに関する情報=HTTPヘッダ)
 …
 …
    • レスポンス(サーバがブラウザに返す応答)
HTTP/1.1 200 OK
(以下、HTTPヘッダ)
 …
 …
GET /members/show?id=123 HTTP/1.1
 …
    • メソッド::リクエストの種類
GET 情報の要求
(WEBサーバからデータを取得)
POST 情報の送信
(WEBサーバの状態を変更)
HEAD ヘッダ部分のみ要求
PUT アップロード
DELETE リソースの削除
TRACE リクエストをそのまま返送
OPTIONS サーバが対応しているメソッドを取得
    • リダイレクション
      新しいURLをブラウザに要求する仕組み。
      サーバのレスポンスにおいて、ステータスコードに「200」ではなく「301」「302」「307」のいずれかを、HTTPヘッダに新しいURLを指定すると、ブラウザは新しいURLでリクエストし直します。
      Railsでは、リダイレクション処理をするためのredirect_toメソッドが用意されています。
  • Railsのリクエスト処理
    1. ブラウザからのリクエストを受信
    2. パスを解析
    3. config\routes.rbに従い、コントローラ/アクションを選択
    4. アクションを実行
    5. (アクションが存在しない場合、アクション名と同名のビューを選択)
    6. ビューを選択
    7. コントローラのインスタンス変数値をHTMLソース内に挿入(=レンダリング
    8. HTMLファイルをブラウザに返送
  • ルーティング
    リクエストされたURLから特定のコントローラ/アクションを選択すること。
    特定のURLと特定のアクションを紐付ける場合、config\routes.rbで設定します。
    • URLのパス形式の設定
      • コントローラ名・アクション名・パラメータを
        http://localhost:3000/コントローラ名/アクション名/パラメータ というURLで表示させます。
      • 複数のパラメータを使った形式も自由に追加できます。上から順に優先されます。
  map.connect ':controller/:action/:year/:month/:date'
  # URLを http://localhost:3000/コントローラ名/アクション名/年/月/日 という形式で表示(最優先)

  map.connect ':controller/:action/:id'
  # URLを http://localhost:3000/コントローラ名/アクション名/id という形式で表示
    • URLとアクションの紐付け
      • 第1引数には、特定のアクションを呼び出すURLのうち、http://localhost:3000/以下の部分を指定します。
      • アクション名がindexの場合は、:action => 'index' を省略できます。コントローラ名は省略できません。
  map.connect '', :controller => 'ctr_name', :action => 'index'
  # http://localhost:3000/ というURLより、ctr_nameコントローラのindexアクションを実行
  # public/index.rhtml が残っていると正しく動作しない(public/index.rhtml削除 ⇒ WEBrick再起動)

  map.connect 'hoge/fuga', :controller => 'piyo'
  # http://localhost:3000/hoge/fuga というURLより、piyoコントローラのindexアクションを実行
    • publicフォルダ
      publicフォルダには、ユーザに送信したいファイル(画像・スタイルシートJavaScript等)を置くことができます。
      Railsでは、リクエストされたパスとpublic内のファイルのパスが一致した場合、public内のファイルをコントローラ/アクションに優先して送信します。

コントローラとアクション

  • コントローラ
class CtrNameController < ApplicationController
  before_filter :before_actions

  def indexend

  def action1end

  def action2end

  private

  def before_actionsend
end
    • アクション=パブリックメソッド[例:index, action1, action2]
      ブラウザから直接実行できるメソッド。
      原則として、同名のテンプレートと1対1で紐付いています。
    • プライベートメソッド[例:before_actions]
      テンプレートを持たないメソッドを、ブラウザから直接実行できないようにしたもの。
    • app\controller\application.rb
      ApplicationControllerはコントローラクラスのスーパークラスです。
      application.rbに記述したメソッドは、すべてのコントローラで使うことができます。
コントローラ(クラス名) HogeFugaController
コントローラ(ファイル名) hoge_fuga_controller.rb
テンプレート(親フォルダ名) app/views/hoge_fuga
ヘルパー(モジュール名) HogeFugaHelper
ヘルパー(ファイル名) hoge_fuga_helper.rb
レイアウトテンプレート(ファイル名) hoge_fuga.rhtml
機能テスト(ファイル名) hoge_fuga_controller_test.rb
  • パラメータ
    アクションの呼出と同時に指定できる変数。
    params[:パラメータ名]の形で取得します。
    パラメータはテンプレート内でも取得できます。
    また、params[:controller]でコントローラ名を、params[:action]でアクション名を取得できます。
  id = params[:id]
  name = params[:name]
  • requestオブジェクト
    リクエスト発信者の情報をもつオブジェクト。
主なメソッド 機能
request.remote_ip リクエスト発信者のIPアドレスを取得
request.env['HTTP_USER_AGENT'] リクエスト発信者のブラウザの種類を取得
request.get? リクエストがGETメソッドかどうかを判別
request.post? リクエストがPOSTメソッドかどうかを判別
  • renderメソッド
    特定のテンプレートをレンダリングさせるメソッド。
    ハッシュの形で引数を指定します。
def action1
  render :template => 'ctr_name/other_template'
  # 別のテンプレートにレンダリング
  # (indexアクションが実行されるとき、テンプレートはctr_name/other_template.rhtml)
  # :templateには、app\viewsを基点としたパスを指定
end

def action2
  render :action => 'action3'
  # 別のアクションのテンプレートにレンダリング
  # (action2アクションが実行されるとき、テンプレートはaction3.rhtml)
  # ただし、action3メソッドが呼び出されるわけではない
end                ^^^^^^^^

def action3end
  render :file => "#{RAILS_ROOT}/public/404.rhtml",   # publicフォルダ下のファイルをそのまま、
         :status => '404 Not Found'                   # HTTPの404エラーとして表示
  • redirect_toメソッド
    HTTPのリダイレクションを実行するメソッド。
    テンプレートのレンダリングとは異なり、新たにURLがリクエストされます。
    引数にはリダイレクション先のアクションを指定するほか、コントローラを指定したり、パラメータを渡すこともできます。
  def before_redirection
    redirect_to :action => 'after_redirection', :message => "リダイレクション"
  end

  def after_redirection
    @message = params[:message]   #=> "リダイレクション"
  end
    • flashオブジェクト
      アクションとアクションの間で情報を受け渡せるオブジェクト。
      リダイレクションの前に値を入れると、その値は【アクション⇒リダイレクション⇒アクション】の処理のあいだ保持され、処理終了以降nilになります。
      セッションを利用した機能であるため、ブラウザのCookieが無効になっていると動作しません。
  def before_redirection
    flash[:notice] = "移動しました"
    redirect_to :action => 'after_redirection'
  end

  def after_redirection
    @notice = flash[:notice]   #=> "移動しました"
    # このアクションをリロードすると、@notice(flash[:notice])はnilになる
  end
    • render/redirect_to の重複エラー
      render/redirect_toメソッドは、1つのメソッドにつき1回しか実行できません。
      もしメソッド内にrender/redirect_toメソッドを複数回記述する必要があるときは(なくても)、render/redirect_toメソッドの後にreturn(=その場でメソッドを終了)を記述しましょう。
  • フィルタ
    • before_filter/after_filte
      アクションの前/後に実行するprivateメソッドを宣言します。
      原則として全アクションに適用されますが、:only/:exceptオプションを利用すれば、適用するアクションを特定することもできます。
class CtrNameController < ApplicationController
  before_filter :before_actions, :only => [:action1, :action3]
  after_filter :after_actions, :except => :action1
  # action1/action3の前にbefore_actionsが、
  # action2/action3の後にafter_actionsが実行される

  def action1end

  def action2end

  def action3end

  private

  def before_actionsend

  def after_actionsend
end
    • around_filter
      ここにprivateメソッドを宣言すると、全アクションにおいて、メソッドの途中でアクションが呼び出されるようになります。
      オプションで適用するアクションを特定することはできません。
class CtrNameController < ApplicationController
  around_filter :around_actions

  def action1end

  def action2end

  def action3end

  private

  def around_actionsyield   # ここで各アクションが呼び出されるend
end
  • verify
    アクションの実行前に指定条件を満たしているか検証し、満たしていない場合は指定処理を実行します。
class CtrNameController < ApplicationController
  verify :only => [:action1, :action2],
         :params => :id,
         :redirect_to => {:action => :index}
  # action1/action2の実行前にparams[:id]があるか検証し、なければindexにリダイレクト

  def indexend

  def action1end

  def action2end

  def action3end
end
    • verifyのオプション
アクションの指定
:only => [:hoge, :fuga...] hogeアクション,fugaアクションのみに適用
:except => [:hoge, :fuga...] hogeアクション,fugaアクション以外に適用
条件
:flash => :hoge flash[:hoge]はあるか?
:method => :get HTTPのメソッドはGETか?
:params => :hoge params[:hoge]はあるか?
:session => :hoge session[:hoge]はあるか?
:xhr => true/false アクションはAJAXから呼ばれたか?
条件を満たしていない場合の処理
:add_flash => {:hoge => "ほげ"} flash[:hoge]に"ほげ"を付与
:add_headers => {:hoge => "ほげ"} HTTPヘッダのhogeフィールドに"ほげ"を付与
:redirect_to => {:action => hoge} hogeアクションにリダイレクト
:render => {:action => hoge} hogeアクションのテンプレートにレンダリング
  • テンプレート
    Railsのページデザインの基本は、コントローラで用意した変数をテンプレートに埋め込む方法です。
    • RHTML
      HTML文書の中にRubyコードを埋め込んだファイルの書式。
      <%= %> または <% %> で囲んだ部分がRuby式として解釈されます。
      <%= %> で囲んだ場合、式の結果を文字列に変換したものが出力されます。
      <% %> で囲んだ場合、出力はされません。
      また、<%= -%> や <% -%> のように書くと、タグの後ろにある改行が取り除かれます。
<% time = Time.now %>
<p>現在、</p>
<p><%= time -%>
です。</p>

 ↓ブラウザ上の出力

現在、
Wed Aug 5 15:46:52 +0900 2009です。
    • コントローラで変数を用意
# controller
def index
  @time = Time.now.strftime("%H:%M")
end
<p>現在、</p>
<p><%= @time %>です。</p>
  • 特殊文字の変換
    HTMLコード上で意味をもつ記号(<, >, & 等)が変数に含まれていた場合、その変数をそのままテンプレートに埋め込むと、意図しないコードが実行されてしまう恐れがあり大変危険です。
    railsのhメソッドは、これらの記号を変換(&lt;, &gt;, &amp; 等)してくれます。
# controller
@danger = "<script>危険なコード</script>"
<%= h(@danger) %>
  • ヘルパーメソッドの作成
    ヘルパーには、テンプレートで利用するためのメソッドを自作することができます。
# helper
def japanese_time(time)
  wday = ['', '', '', '', '', '', '']
  time.strftime('%Y/%m/%d<') + wday[time.wday] + time.strftime('> %H:%M:%S')
end
# controller
def index
  @time = Time.now
end
<p>現在、</p>
<p><%= japanese_time(@time) %>です。</p>

 ↓ブラウザ上の出力

現在、
2009/08/05<水> 15:46:52です。
    • よく使われるヘルパーメソッド
      • link_to::指定したアクションへのリンクタグを生成
      • image_tag::指定した画像の表示タグを生成
      • stylesheet_link_tag::指定したスタイルシートを適用
  • レイアウトテンプレート
    app\views\layoutsの下に作成します。
    • application.rhtml
      アプリケーション全体で共通のレイアウトテンプレート。
      文書型宣言(使用している言語型=HTML/XHTMLやそれらのバージョンの宣言)や文字コード指定などもここに記述してしまえば、他のテンプレートファイルでは不要です。
<!DOCTYPE …>
<html>
<head>
  <meta … />
  <title></title>
</head>
<body><%= yield :layout %>   <!-- アクション毎のテンプレート埋め込み --></body>
</html>
    • ctr_name.rhtml::CtrNameControllerで共通のレイアウトテンプレート
      また、使用したいレイアウトテンプレートをコントローラ内で宣言することもできます。
class CtrNameController < ApplicationController
  layout 'other_layout'   # other_layout.rhtmlをレイアウトテンプレートとして使用
end  
  • 部分テンプレート
    ファイル名は、_hoge.rhtmlのように_(アンダーバー)で始め、app\views\コントローラ名の下に作成します。
    複数のコントローラで共有したい場合は、app\views\shared(あるいは任意のフォルダ名))の下に作成します。
    部分テンプレートを呼び出したいビューの中で、次のように、ファイル名から_(アンダーバー)と拡張子を除いたものを指定します。
<%= render :partial => 'hoge' %>
<%= render :partial => 'shared/hoge' %>
div#hoge a:link{
  color: #000000
}
<div id="hoge">
  <%= link_to "ほげへのリンク", :action => 'hoge' %>
</div>

id属性"hoge"で囲まれた部分にCSSが適用されます。
また、子孫セレクタ(div#id属性名 要素名)を活用すれば、id属性に加えて要素ごとのデザインまで設定することができます。
上記の場合、ブラウザ上では、"ほげへのリンク"が黒色(#000000)で表示されます。