icon Ruby on Rails 2.0 日记

第6章 redirect_to 与 url_for

这一章关于 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) } 这一行。对象 ActiveRecord 被发送到 redirect_to 参数。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

因为 ActiveRecord 对象被发送到参数 options ,所以 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) ,很费了一番功夫。大家辛苦了!

(2008/02/24)