第6回 redirect_to と url_for

2008/02/24

今回は、redirect_to メソッドと url_for メソッドについて。REST と関係のある話です。

まず、単純な User モデルのための scaffold を作ります。

ruby script/generate scaffold user name:string

app/controllers/users_controller.rbcreate アクションは次のようになります。

  # POST /users
  # POST /users.xml
  def create
    @user = User.new(params[:user])

    respond_to do |format|
      if @user.save
        flash[:notice] = 'User was successfully created.'
        format.html { redirect_to(@user) }
        format.xml  { render :xml => @user, :status => :created, :location => @user }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @user.errors, :status => :unprocessable_entity }
      end
    end
  end

format.html { redirect_to(@user) } の行に注目してください。redirect_to の引数に ActiveRecord オブジェクトが渡されています。Rails 1.2.x ではこのような書き方はできませんでした。同じことをするなら format.html { redirect_to user_url(@user) } と書く必要がありました。これは進歩ですね。

Rails のソースコードを見てみることにしましょう。

まずは、Rails 1.2.6 (actionpack-1.13.6) のコードから。

      def redirect_to(options = {}, *parameters_for_method_reference) #:doc:
        case options
          when %r{^\w+://.*}
            # (省略)
          when String
            # (省略)
          when :back
            # (省略)
          else
            if parameters_for_method_reference.empty?
              redirect_to(url_for(options))
              response.redirected_to = options
            else
              # (省略)
            end
        end
      end

引数に ActiveRecord オブジェクトを渡した場合、redirect_to(url_for(options)) が実行されます。しかし、Rails 1.2.6 の url_for メソッドは ActiveRecord オブジェクトを処理できません。

続いて、Rails 2.0.2 (actionpack-2.0.2) のコード。

      def redirect_to(options = {}, response_status = {}) #:doc: 
        # (省略)
        
        case options
          when %r{^\w+://.*}
            # (省略)
          when String
            # (省略)
          when :back
            # (省略)
          when Hash
            # (省略)
          else
            redirect_to(url_for(options), :status=>status)
        end
      end

引数に ActiveRecord オブジェクトを渡した場合、最後の redirect_to(url_for(options), :status=>status) が実行されます。url_for メソッドで URL を作っています。

では、url_for メソッドを見てみましょう。

      def url_for(options = nil) #:doc:
        case options || {}
          when String
            options
          when Hash
            @url.rewrite(rewrite_options(options))
          else
            polymorphic_url(options)
        end
      end

引数 options に ActiveRecord オブジェクトを渡したわけですから、else 節の polymorphic_url(options) が実行されます。

このメソッドが定義されているのは、polymorphic_routes.rb です。

    def polymorphic_url(record_or_hash_or_array, options = {})
      # (省略)
            
      named_route = build_named_route_call(record_or_hash_or_array, namespace, inflection, options)
      send!(named_route, *args)
    end

下から3行目の build_named_route_call メソッドは、'user_url' という文字列を返します。redirect_to user_url(@user)redirect_to(@user) と短くするために、随分とがんばったものです。お疲れ様!