ITコンサルの日常

ITコンサル会社に勤務する普通のITエンジニアの日常です。

「RailsによるアジャイルWebアプリケーション開発」21章読み中

ActionControllerの続き。

コントローラクラス内のpublicメソッドをactionとして使わない方法。

通常であれば、privateメソッド、もしくは、protectedメソッドとして隠します。

private
  # DELETE /products/1
  # DELETE /products/1.xml
  def destroy
    @product = Product.find(params[:id])
    @product.destroy

    respond_to do |format|
      format.html { redirect_to(products_url) }
      format.xml  { head :ok }
    end
  end

この状態で、destoryアクションを呼ぶと、Unknown action (No action responded to destory)となります。


じゃあ、publicのままで、でも、アクションとして使いたくない場合は、hide_actionを使います。

  hide_action :destroy

  # DELETE /products/1
  # DELETE /products/1.xml
  def destroy
    @product = Product.find(params[:id])
    @product.destroy

    respond_to do |format|
      format.html { redirect_to(products_url) }
      format.xml  { head :ok }
    end
  end

同じく、Unknown actionとなります。
ただ、本書中にもあるように、コントローラ中にアクションでないpublicメソッドがある場合は、リファクタリングを検討した方が良さそうです。

Unknown actionが発生した時にデバッグ情報を出力する

render(:inline => string, [:type => "rhtml"|"rxml"|"rjs"], [:locals => hash])
を使うとできます。

  if RAILS_ENV == "development"
    def method_missing(name, *args)
      render(:inline => %{
        <h2>Invalid Action: #{name}</h2>
        request parameter is :<br/>
        <%= debug(params) %> })
    end
  end

if RAILS_ENV == "development"は、開発モードのみという判定。

上記のコードをコントローラ(products_controller.rb)の一番下に入れて、
試しにhttp://localhost:3000/products/hoge/1にアクセスしてみると、以下のように表示されました。

Invalid Action: hoge
request parameter is :

--- !map:HashWithIndifferentAccess 
action: hoge
id: "1"
controller: products

デバッグに役立ちそうですね。

ファイルをダウンロード

send_dataとか、send_fileというのを使うらしい。

試しに、products_controller.rbに以下のようなメソッドを追加。

  def index_download
    @products = Product.find(:all)
  
    send_data(@products, :filename => "products.txt")
  end

で、
http://localhost:3000/products/index_download/1
にアクセス。
すると、



おお!出た出た。
割りと簡単なのね。
で、早速ダウンロードしたファイルを開いてみる。

#<Product:0xb7798cb0>

...全く面白くないが、ダウンロードさせるっていう目的はいいみたい。


つまらないので、CSV形式(タイトル,URL)にしてみる。
products_controller.rbを以下のように修正。

  def index_download
    @products = Product.find(:all)

    data = ""
    @products.each { |p|
      data = data + p.title + "," + p.image_url
      data = data + "\n"
    }

    send_data(data, :filename => "products.txt")
  end

別にどうってことはないのですが、ちょっとは実用的になったかも。
URLはちょっとヘンですが。。


(2008/07/24 追記)
routes.rbに以下の行を追加。

  map.connect ':controller/index_download', :action => "index_download"

map.resources :products
の行の前に入れないとダメです。
これで、
http://localhost:3000/products/index_download
と、/1なしのURLでもindexをダウンロードすることが出来るようになりました。

301 Moved Permanentlyを返す(つもりが出来てない?)

products_controller.rbにこんなアクションを追加。

  def moved
   headers["Status"] = "301 Moved Permanently"
   redirect_to("http://www.yahoo.co.jp/")
  end

で、
http://localhost:3000/products/moved/1
にアクセス。
すると、Yahoo Japanのホームページに飛びました。


一見うまくいったように見えるものの、Live HTTP Headersで見てみると、
なぜか
HTTP/1.x 302 Found

が返ってきてる。なんでだろ。
Rails的に301よりも302の方が適切っていう判断なのかしら。

(2008/07/24 追記)
コメント欄で指摘いただきましたが、

  def moved
#   headers["Status"] = "301 Moved Permanently"
   redirect_to("http://www.yahoo.co.jp/", :status => 301)
  end

でうまくいきました。